> ## Documentation Index
> Fetch the complete documentation index at: https://muveya.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Guia rápido da API

> Faça suas primeiras chamadas à API do muveya com cURL, JavaScript ou Python: identifique sua chave, liste unidades e insumos do catálogo, leia um pedido e percorra páginas.

Esta página leva você de uma chave de API nova até uma lista paginada de pedidos. Todos os exemplos rodam no seu servidor, nunca em um navegador.

## Antes de começar

* Uma chave de API com os escopos `clinics:read`, `catalog:read` e `orders:read`. Veja [Autenticação](/docs/pt/api-reference/authentication) para saber como obtê-la.
* Um destes: `curl`, Node.js 18 ou superior (que já inclui `fetch`), ou Python 3 com o pacote `requests` (`pip install requests`).
* Os exemplos em JavaScript usam `await` no nível superior: salve-os em um arquivo terminado em `.mjs`.

## Passos

<Steps>
  <Step title="Coloque a chave em uma variável de ambiente">
    Nunca escreva a chave no seu código. Exporte-a no terminal (ou carregue-a do seu gerenciador de segredos):

    ```bash theme={null}
    export MUVEYA_API_KEY="mvy_test_EXAMPLE..."
    ```

    Todos os exemplos abaixo leem `MUVEYA_API_KEY` e chamam `https://api.muveya.com`.
  </Step>

  <Step title="Identifique a chave">
    `GET /v1/me` não exige escopo. Ela informa qual clínica odontológica a chave lê e quais escopos ela tem.

    <CodeGroup>
      ```bash cURL theme={null}
      curl https://api.muveya.com/v1/me \
        -H "Authorization: Bearer $MUVEYA_API_KEY"
      ```

      ```javascript JavaScript theme={null}
      const BASE_URL = "https://api.muveya.com";
      const headers = { Authorization: `Bearer ${process.env.MUVEYA_API_KEY}` };

      const response = await fetch(`${BASE_URL}/v1/me`, { headers });
      if (!response.ok) {
        const problem = await response.json();
        throw new Error(`${problem.status} ${problem.code} (request ${problem.requestId})`);
      }
      console.log(await response.json());
      ```

      ```python Python theme={null}
      import os
      import requests

      BASE_URL = "https://api.muveya.com"
      session = requests.Session()
      session.headers["Authorization"] = f"Bearer {os.environ['MUVEYA_API_KEY']}"

      response = session.get(f"{BASE_URL}/v1/me", timeout=30)
      if not response.ok:
          problem = response.json()
          raise RuntimeError(f"{problem['status']} {problem['code']} (request {problem['requestId']})")
      print(response.json())
      ```
    </CodeGroup>

    ```json Resposta theme={null}
    {
      "workspaceId": "66d0a1f0c0ffee0000000001",
      "environment": "test",
      "scopes": ["clinics:read", "catalog:read", "orders:read"]
    }
    ```

    Um `401` com `api_keys.invalid` significa que o cabeçalho ou a chave estão errados. Veja [Respostas 401 e 403](/docs/pt/api-reference/authentication#respostas-401-e-403).
  </Step>

  <Step title="Liste suas unidades">
    `GET /v1/clinics` (escopo `clinics:read`) retorna todas as unidades da clínica odontológica em uma única página. `GET /v1/warehouses` funciona do mesmo jeito para os depósitos.

    <CodeGroup>
      ```bash cURL theme={null}
      curl https://api.muveya.com/v1/clinics \
        -H "Authorization: Bearer $MUVEYA_API_KEY"
      ```

      ```javascript JavaScript theme={null}
      const clinics = await fetch(`${BASE_URL}/v1/clinics`, { headers }).then((r) => r.json());
      for (const clinic of clinics.data) {
        console.log(clinic.clinicId, clinic.name, clinic.status);
      }
      ```

      ```python Python theme={null}
      clinics = session.get(f"{BASE_URL}/v1/clinics", timeout=30).json()
      for clinic in clinics["data"]:
          print(clinic["clinicId"], clinic["name"], clinic["status"])
      ```
    </CodeGroup>

    ```json Resposta theme={null}
    {
      "data": [
        { "clinicId": "66d0a1f0c0ffee0000000101", "name": "Clínica Norte", "status": "active" },
        { "clinicId": "66d0a1f0c0ffee0000000102", "name": "Clínica Sul", "status": "active" }
      ],
      "hasMore": false
    }
    ```

    Mantenha um mapa de `clinicId` para `name`: pedidos e análises se referem às unidades pelo id.
  </Step>

  <Step title="Liste os insumos do catálogo">
    `GET /v1/catalog/items` (escopo `catalog:read`) retorna todos os insumos do catálogo, qualquer que seja o `status`, em uma única página.

    <CodeGroup>
      ```bash cURL theme={null}
      curl https://api.muveya.com/v1/catalog/items \
        -H "Authorization: Bearer $MUVEYA_API_KEY"
      ```

      ```javascript JavaScript theme={null}
      const items = await fetch(`${BASE_URL}/v1/catalog/items`, { headers }).then((r) => r.json());
      const active = items.data.filter((item) => item.status === "active");
      console.log(`${active.length} active items`);
      ```

      ```python Python theme={null}
      items = session.get(f"{BASE_URL}/v1/catalog/items", timeout=30).json()
      active = [item for item in items["data"] if item["status"] == "active"]
      print(f"{len(active)} active items")
      ```
    </CodeGroup>

    ```json Resposta theme={null}
    {
      "data": [
        {
          "itemId": "66d0a1f0c0ffee0000000401",
          "sku": "GLV-NIT-M",
          "name": "Nitrile gloves, size M",
          "categoryId": "66d0a1f0c0ffee0000000301",
          "unitOfMeasure": "box",
          "packaging": "Box of 100 gloves",
          "criticality": "high",
          "tracksLot": true,
          "tracksSerial": false,
          "tracksExpiry": true,
          "highValue": false,
          "status": "active"
        }
      ],
      "hasMore": false
    }
    ```

    Esta chave não tem `catalog.cost:read`, então os campos de custo ficam ausentes. Com esse escopo, cada insumo também traz `costStatus` e, quando há um custo registrado, `cost`, `currency` e `costMeasurement`. Confira `costStatus` antes de usar um valor: veja [Custos](/docs/pt/catalog/costs).
  </Step>

  <Step title="Leia um pedido">
    Pegue um `orderId` de `GET /v1/orders` e leia-o com `GET /v1/orders/{orderId}` (escopo `orders:read`). O detalhe inclui as linhas e o plano de aprovação.

    <CodeGroup>
      ```bash cURL theme={null}
      curl https://api.muveya.com/v1/orders/66d0a1f0c0ffee0000000501 \
        -H "Authorization: Bearer $MUVEYA_API_KEY"
      ```

      ```javascript JavaScript theme={null}
      const orderId = "66d0a1f0c0ffee0000000501";
      const response = await fetch(`${BASE_URL}/v1/orders/${encodeURIComponent(orderId)}`, { headers });
      if (response.status === 404) {
        console.log("No such order in this dental clinic");
      } else {
        const order = await response.json();
        console.log(order.number, order.status, order.lines.length);
      }
      ```

      ```python Python theme={null}
      order_id = "66d0a1f0c0ffee0000000501"
      response = session.get(f"{BASE_URL}/v1/orders/{order_id}", timeout=30)
      if response.status_code == 404:
          print("No such order in this dental clinic")
      else:
          order = response.json()
          print(order["number"], order["status"], len(order["lines"]))
      ```
    </CodeGroup>

    ```json Resposta theme={null}
    {
      "orderId": "66d0a1f0c0ffee0000000501",
      "number": 42,
      "type": "general",
      "status": "pending_approval",
      "requesterId": "66d0a1f0c0ffee0000000701",
      "clinicId": "66d0a1f0c0ffee0000000101",
      "destinationWarehouseId": "66d0a1f0c0ffee0000000202",
      "justification": "Weekly restock for the surgery rooms",
      "lines": [
        {
          "lineId": "66d0a1f0c0ffee0000000601",
          "catalogItemId": "66d0a1f0c0ffee0000000401",
          "sku": "GLV-NIT-M",
          "name": "Nitrile gloves, size M",
          "requestedQty": 12,
          "unitOfMeasure": "box",
          "category": "66d0a1f0c0ffee0000000301",
          "highValue": false
        }
      ],
      "approvalPlan": {
        "policyVersion": 3,
        "requiresApproval": true,
        "steps": [
          { "stage": "manager", "requiredScope": "approvals.decide", "minApprovers": 1 }
        ]
      },
      "version": 2
    }
    ```

    O `value` do pedido e o `patientRef` de um pedido clínico nunca são retornados em `/v1`. Um pedido de outra clínica odontológica, ou um id que não existe, responde `404` com o código `orders.not_found`.
  </Step>

  <Step title="Percorra as páginas">
    `GET /v1/orders` é paginado. Peça até 200 pedidos por página e envie `nextCursor` de volta como `cursor` enquanto `hasMore` for `true`. Envie os mesmos filtros em todas as páginas.

    <CodeGroup>
      ```bash cURL theme={null}
      # First page
      curl "https://api.muveya.com/v1/orders?limit=2&status=approved" \
        -H "Authorization: Bearer $MUVEYA_API_KEY"

      # Next page: copy nextCursor from the previous response
      curl "https://api.muveya.com/v1/orders?limit=2&status=approved&cursor=MjAyNi0wOS0xMlQxNDowNTowMC4wMDBafDY2ZDBhMWYwYzBmZmVlMDAwMDAwMDQ5OQ" \
        -H "Authorization: Bearer $MUVEYA_API_KEY"
      ```

      ```javascript JavaScript theme={null}
      async function listAllOrders(status) {
        const orders = [];
        let cursor;
        do {
          const url = new URL("/v1/orders", BASE_URL);
          url.searchParams.set("limit", "200");
          if (status) url.searchParams.set("status", status);
          if (cursor) url.searchParams.set("cursor", cursor);

          const response = await fetch(url, { headers });
          if (!response.ok) throw new Error(`HTTP ${response.status}`);
          const page = await response.json();

          orders.push(...page.data);
          cursor = page.hasMore ? page.nextCursor : undefined;
        } while (cursor);
        return orders;
      }

      const approved = await listAllOrders("approved");
      console.log(`${approved.length} approved orders`);
      ```

      ```python Python theme={null}
      def list_all_orders(status=None):
          orders, cursor = [], None
          while True:
              params = {"limit": 200}
              if status:
                  params["status"] = status
              if cursor:
                  params["cursor"] = cursor
              response = session.get(f"{BASE_URL}/v1/orders", params=params, timeout=30)
              response.raise_for_status()
              page = response.json()
              orders.extend(page["data"])
              if not page["hasMore"]:
                  return orders
              cursor = page["nextCursor"]

      approved = list_all_orders("approved")
      print(f"{len(approved)} approved orders")
      ```
    </CodeGroup>

    ```json Primeira página theme={null}
    {
      "data": [
        {
          "orderId": "66d0a1f0c0ffee0000000498",
          "number": 40,
          "type": "general",
          "status": "approved",
          "requesterId": "66d0a1f0c0ffee0000000701",
          "clinicId": "66d0a1f0c0ffee0000000101",
          "destinationWarehouseId": "66d0a1f0c0ffee0000000202",
          "version": 3
        },
        {
          "orderId": "66d0a1f0c0ffee0000000499",
          "number": 41,
          "type": "general",
          "status": "approved",
          "requesterId": "66d0a1f0c0ffee0000000702",
          "clinicId": "66d0a1f0c0ffee0000000102",
          "destinationWarehouseId": "66d0a1f0c0ffee0000000203",
          "version": 3
        }
      ],
      "hasMore": true,
      "nextCursor": "MjAyNi0wOS0xMlQxNDowNTowMC4wMDBafDY2ZDBhMWYwYzBmZmVlMDAwMDAwMDQ5OQ"
    }
    ```

    Os pedidos vêm do mais antigo para o mais recente. O cursor é opaco: não o decodifique nem o construa. Veja [Paginação](/docs/pt/api-reference/pagination) para todas as regras.
  </Step>
</Steps>

## Trate erros e limites

Dois hábitos deixam uma integração robusta desde o primeiro dia:

1. **Decida pelo `code`, não pelo texto.** Todo erro é um documento de problema com um `code` estável e um `requestId`. Registre os dois. Veja [Erros](/docs/pt/api-reference/errors).
2. **Respeite o limite de uso.** Cada chave tem um orçamento de requisições por minuto. Diante de um `429`, espere os segundos indicados em `Retry-After` e tente de novo. Veja [Limites de uso](/docs/pt/api-reference/rate-limits).

## Próximos passos

<CardGroup cols={2}>
  <Card title="Escolha os escopos" icon="shield-halved" href="/docs/pt/api-reference/scopes">
    Receitas de privilégio mínimo para integrações comuns.
  </Card>

  <Card title="Exporte o resumo gerencial" icon="file-csv" href="/docs/pt/api-reference/exports">
    Peça um CSV, acompanhe o status e verifique o checksum.
  </Card>

  <Card title="Fique em dia sem webhooks" icon="bell" href="/docs/pt/api-reference/webhooks">
    Receitas de consulta periódica para pedidos, entregas e estoque.
  </Card>

  <Card title="Conecte um assistente de IA" icon="plug" href="/docs/pt/mcp/introduction">
    O MCP usa um fluxo separado de login e autorização com OAuth.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.