> ## 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.

# Paginação

> Percorra as listas paginadas de /v1 com limit e um cursor opaco, e conheça a ordem e os filtros de cada lista.

Toda lista de `/v1` retorna o mesmo envelope, paginada ou não:

```json theme={null}
{
  "data": [],
  "hasMore": true,
  "nextCursor": "NjZkMGExZjBjMGZmZWUwMDAwMDAwODAy"
}
```

| Campo | Significado |
| - | - |
| `data` | Os itens desta página. |
| `hasMore` | `true` quando há pelo menos mais uma página. |
| `nextCursor` | O token da próxima página. Aparece **somente** quando `hasMore` é `true`; caso contrário, o campo fica ausente (não é `null`). |

## Quais listas são paginadas

| Operação | Paginada | Ordem | Filtros |
| - | - | - | - |
| `GET /v1/orders` | Sim | Do mais antigo para o mais recente, pela data de criação | `status` |
| `GET /v1/fulfillments` | Sim | Do mais antigo para o mais recente, pela data de criação | `status` |
| `GET /v1/inventory/balances` | Sim | Pelo id da caixa, crescente | `catalogItemId`, `warehouseId` |
| `GET /v1/clinics` | Não, uma única página | Ordem de criação | Nenhum |
| `GET /v1/warehouses` | Não, uma única página | Ordem de criação | Nenhum |
| `GET /v1/catalog/items` | Não, uma única página | Ordem de criação | Nenhum |
| `GET /v1/catalog/categories` | Não, uma única página | Ordem de criação | Nenhum |
| `GET /v1/inventory/boxes/{boxId}/movements` | Não, uma única página | Do mais antigo para o mais recente, por `occurredAt` | Nenhum |

As listas não paginadas retornam todos os itens em uma única página com `"hasMore": false` e ignoram `limit` e `cursor`. Escreva o mesmo laço para todas as listas mesmo assim: se alguma delas passar a ser paginada, seu código continua funcionando sem mudanças.

## Parâmetros de consulta

<ParamField query="limit" type="integer" default="50">
  Quantos itens retornar, de 1 a 200. Apenas dígitos são aceitos. Um 0, um número negativo, um decimal, um texto ou um número acima de 200 é recusado com `400` e o código `common.invalid_request`.
</ParamField>

<ParamField query="cursor" type="string">
  O `nextCursor` da página anterior, copiado exatamente. Um cursor que não pode ser lido é recusado com `400` e o código `common.invalid_request`; o servidor nunca volta silenciosamente à primeira página.
</ParamField>

<ParamField query="status" type="string">
  Apenas em `GET /v1/orders` e `GET /v1/fulfillments`. Precisa ser um dos valores de status listados para essa operação no grupo **Endpoints** (por exemplo `pending_approval` para pedidos ou `dispatched` para atendimentos). Qualquer outro valor é recusado com `400` e o código `common.invalid_request`.
</ParamField>

<ParamField query="catalogItemId" type="string">
  Apenas em `GET /v1/inventory/balances`: mantém as caixas de um insumo do catálogo. Um id que não corresponde a nada retorna uma página vazia.
</ParamField>

<ParamField query="warehouseId" type="string">
  Apenas em `GET /v1/inventory/balances`: mantém as caixas de um depósito. Um id que não corresponde a nada retorna uma página vazia.
</ParamField>

## Percorra todas as páginas

<CodeGroup>
  ```bash cURL theme={null}
  # First page
  curl "https://api.muveya.com/v1/inventory/balances?limit=200&warehouseId=66d0a1f0c0ffee0000000201" \
    -H "Authorization: Bearer $MUVEYA_API_KEY"

  # Next page: same filters, plus the cursor you received
  curl "https://api.muveya.com/v1/inventory/balances?limit=200&warehouseId=66d0a1f0c0ffee0000000201&cursor=NjZkMGExZjBjMGZmZWUwMDAwMDAwODAy" \
    -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}` };

  async function* pages(path, filters = {}) {
    let cursor;
    do {
      const url = new URL(path, BASE_URL);
      url.searchParams.set("limit", "200");
      for (const [name, value] of Object.entries(filters)) url.searchParams.set(name, value);
      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();
      yield page.data;
      cursor = page.hasMore ? page.nextCursor : undefined;
    } while (cursor);
  }

  let boxes = 0;
  for await (const data of pages("/v1/inventory/balances", { warehouseId: "66d0a1f0c0ffee0000000201" })) {
    boxes += data.length;
  }
  console.log(`${boxes} boxes`);
  ```

  ```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']}"

  def pages(path, **filters):
      cursor = None
      while True:
          params = {"limit": 200, **filters}
          if cursor:
              params["cursor"] = cursor
          response = session.get(f"{BASE_URL}{path}", params=params, timeout=30)
          response.raise_for_status()
          page = response.json()
          yield page["data"]
          if not page["hasMore"]:
              return
          cursor = page["nextCursor"]

  boxes = sum(len(data) for data in pages("/v1/inventory/balances", warehouseId="66d0a1f0c0ffee0000000201"))
  print(f"{boxes} boxes")
  ```
</CodeGroup>

```json Última página theme={null}
{
  "data": [
    {
      "boxId": "66d0a1f0c0ffee0000000803",
      "catalogItemId": "66d0a1f0c0ffee0000000401",
      "warehouseId": "66d0a1f0c0ffee0000000201",
      "onHand": 7,
      "reserved": 2,
      "available": 5
    }
  ],
  "hasMore": false
}
```

## Regras do cursor

* **O cursor é opaco.** Trate-o como um token. Não o decodifique, não o construa nem o altere; o formato pode mudar sem aviso.
* **Um cursor marca uma posição, não um número de página.** A próxima página começa logo depois do último item que você recebeu. Itens criados enquanto você percorre a lista não são pulados nem repetidos: pedidos e atendimentos novos aparecem no fim, porque essas listas vão do mais antigo para o mais recente.
* **Envie os mesmos filtros em todas as páginas.** O cursor guarda apenas a posição. Se você mudar `status`, `catalogItemId` ou `warehouseId` no meio do caminho, a próxima página aplica os filtros novos a partir dessa posição.
* **Os filtros são aplicados em cada requisição.** Se um pedido mudar de status enquanto você percorre `GET /v1/orders?status=approved`, ele pode sair do filtro antes de você chegar a ele.
* **Use um cursor apenas com a operação que o emitiu.** Um cursor de uma lista não serve em outra.
* **Use `limit=200` para varreduras completas.** Menos páginas, maiores, consomem menos do seu [limite de uso](/docs/pt/api-reference/rate-limits).

## Erros

| Status | `code` | Causa |
| - | - | - |
| `400` | `common.invalid_request` | `limit` fora do intervalo ou não inteiro, `cursor` ilegível ou `status` desconhecido |
| `401` | `api_keys.invalid` | Veja [Autenticação](/docs/pt/api-reference/authentication#respostas-401-e-403) |
| `403` | `api_keys.scope_missing` | A chave não tem o escopo da lista (veja [Escopos](/docs/pt/api-reference/scopes)) |
| `429` | `common.too_many_requests` | Veja [Limites de uso](/docs/pt/api-reference/rate-limits) |

## Páginas relacionadas

* [Guia rápido da API](/docs/pt/api-reference/quickstart)
* [Webhooks](/docs/pt/api-reference/webhooks): como consultar listas periodicamente para ficar em dia
* [Erros](/docs/pt/api-reference/errors)


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