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

# Erros, novas tentativas e paginação

> Capture erros tipados, tente novamente escritas com segurança usando chaves de idempotência e leia todas as páginas de uma lista.

## Capture um erro

Toda solicitação que falha lança uma subclasse de `PolymorfaError`:

```typescript theme={null}
import {
  PolymorfaConflictError,
  PolymorfaError,
  PolymorfaPaymentRequiredError,
  PolymorfaRateLimitError,
} from "@polymorfa/sdk";

try {
  await platform.sessions.start("<number-id>", { projectId: "<project-id>" });
} catch (error) {
  if (error instanceof PolymorfaPaymentRequiredError) {
    console.error("Add credit before starting this Number", error.requestId);
  } else if (error instanceof PolymorfaConflictError) {
    console.error("The Number changed state", error.code);
  } else if (error instanceof PolymorfaRateLimitError) {
    console.error("Rate limited", error.rateLimitReason, error.metadata?.headers["retry-after"]);
  } else if (error instanceof PolymorfaError) {
    console.error(error.status, error.code, error.message, error.requestId);
  } else {
    throw error;
  }
}
```

Cada erro tem estes campos:

| Campo | Contém |
| - | - |
| `status` | Status HTTP, quando a API respondeu. |
| `code` | Código de erro estável, como `missing_scope`. Veja [Erros](/api/errors). |
| `message` | Descrição legível. |
| `requestId` | ID da solicitação. Inclua-o quando entrar em contato com o suporte. |
| `requestLogUrl` | Página do Console para esta solicitação, quando a API retorna uma. |
| `docUrl` | Link da documentação para `code`. |
| `rateLimitReason` | Por que um `429` aconteceu. |
| `details` | Dados extras do erro enviados pela API. |
| `metadata` | Status da resposta, cabeçalhos e número de tentativas. |

Os códigos de erro nunca mudam de significado. A API pode adicionar novos
códigos, então trate códigos desconhecidos. `isKnownPolymorfaErrorCode(code)`
informa se o SDK instalado conhece um código.

## Classes de erro

| Classe | Quando |
| - | - |
| `PolymorfaValidationError` | `400`, `413` ou `422`: a solicitação é inválida. |
| `PolymorfaAuthenticationError` | `401`: a chave está ausente, é inválida, expirou ou foi revogada. |
| `PolymorfaPaymentRequiredError` | `402`: a equipe não pode pagar pela solicitação. |
| `PolymorfaAuthorizationError` | `403`: falta uma permissão à chave, ou o recurso não está ativado para a equipe. |
| `PolymorfaNotFoundError` | `404`: o recurso não existe ou a chave não consegue vê-lo. |
| `PolymorfaConflictError` | `409`: o recurso mudou, ou uma chave de idempotência foi reutilizada. |
| `PolymorfaRateLimitError` | `429`: solicitações demais. |
| `PolymorfaServerError` | `5xx`: a API falhou. |
| `PolymorfaTimeoutError` | Nenhuma resposta dentro de `timeoutMs`. |
| `PolymorfaConnectionError` | O SDK não conseguiu alcançar a API. |
| `PolymorfaCancelledError` | O seu `signal` cancelou a solicitação. |
| `PolymorfaConfigurationError` | Opções do cliente ou argumentos inválidos. O SDK lança esse erro antes de enviar. |
| `WebhookSignatureError` | A assinatura de um webhook não confere. |
| `PolymorfaMediaIntegrityError` | A mídia do WhatsApp baixada falhou nas verificações de integridade. |

### 402: a equipe não pode pagar

Ações pagas, como iniciar um Número pago, reservam crédito primeiro. Um `402`
significa que a equipe não pode pagar pela solicitação ou não tem o direito de
uso necessário. Tentar novamente não resolve. Verifique o saldo e o plano no
Console e depois tente de novo. O SDK nunca tenta novamente um `402`.

### 409: o recurso mudou

Configurações com revisão, como as configurações de chamadas e a configuração
do Número, falham com `state_conflict` quando outra pessoa as alterou depois que
você as leu. Leia o valor atual, aplique sua alteração de novo e envie a nova
revisão.

Um `409` com um código `idempotency_*` diz respeito à sua chave de idempotência.
Veja [Tente novamente uma escrita com segurança](#tente-novamente-uma-escrita-com-segurança).

### 429: limite de taxa atingido

`rateLimitReason` informa qual limite foi aplicado, como `request_rate` ou
`whatsapp`. Aguarde o tempo do cabeçalho `retry-after` antes de enviar de novo.
O SDK já faz isso nas solicitações que ele tenta novamente. Veja
[Limites de taxa](/api/rate-limits).

## Novas tentativas automáticas

O SDK tenta novamente uma solicitação quando as duas condições são verdadeiras:

* A solicitação é um `GET`, `HEAD` ou `OPTIONS`, ou tem uma `idempotencyKey`.
* Ela falhou com um erro de rede, um timeout ou o status `408`, `409`, `429` ou
  `5xx`.

Ele aguarda o `Retry-After` quando a API o envia, até 60 segundos. Caso
contrário, aumenta a espera exponencialmente, até 5 segundos entre tentativas.
Por padrão, ele tenta novamente duas vezes. Defina `maxNetworkRetries` no
cliente ou na solicitação para mudar isso, ou use `0` para desativar as novas
tentativas.

Uma escrita sem chave de idempotência é enviada uma única vez. O SDK não tenta
novamente essa escrita, porque não consegue saber se a primeira tentativa foi
executada.

## Tente novamente uma escrita com segurança

Uma chave de idempotência torna uma escrita segura para repetir. A API a executa
uma vez e retorna o resultado armazenado em cada repetição com a mesma chave:

```typescript theme={null}
await messaging.messages.send(
  "<number-id>",
  { conversation: { phoneNumber: "+15550001111" }, content: { text: "Your order shipped" } },
  { idempotencyKey: "order-1042-shipped" },
);
```

Use um ID do seu próprio sistema, como o ID de um evento de pedido, para que um
processo reiniciado envie a mesma chave. A API guarda cada chave por 24 horas
por credencial.

Estes métodos criam uma chave aleatória quando você não passa uma, então as
novas tentativas automáticas deles são sempre seguras: `messages.send`,
`messages.react`, `chats.editMessage`, `chats.deleteMessage`,
`channels.reactToMessage`, `campaigns.create`, `campaigns.launch`,
`campaigns.pause`, `campaigns.resume`, `campaigns.stop`, `webhooks.test` e
`operations.cancel`.

| Código | Significado | O que fazer |
| - | - | - |
| `idempotency_conflict` | A chave foi usada em uma solicitação diferente. | Use uma nova chave para uma nova solicitação. |
| `idempotency_in_progress` | A primeira solicitação ainda está em execução. | O SDK aguarda e tenta novamente. |
| `idempotency_completed` | A primeira solicitação já teve sucesso, mas a resposta dela se perdeu. | Leia o recurso em vez de escrever de novo. |
| `idempotency_outcome_unknown`, `result_unknown` | O resultado da primeira tentativa é desconhecido. | Verifique os eventos ou o recurso antes de enviar de novo com uma nova chave. |

## Leia todas as páginas

Listas de eventos, webhooks, entregas de webhook, operações, registros de
chamadas e opt-outs de chamadas retornam uma `CursorPage`. Percorra-a com
`for await` e o SDK busca as próximas páginas para você:

```typescript theme={null}
for await (const event of await project.events.list({ type: "message.received" })) {
  console.log(event.id, event.createdAt);
}
```

Para buscar uma página por vez, use `items`, `hasMore` e `nextPage()`:

```typescript theme={null}
let page: Awaited<ReturnType<typeof project.events.list>> | null =
  await project.events.list({ limit: 100 });

while (page) {
  for (const event of page.items) console.log(event.id);
  page = await page.nextPage();
}
```

`nextPage()` retorna `null` depois da última página. `page.response.metadata`
contém os metadados da resposta.

Alguns métodos retornam o envelope de página da API diretamente, como
`{ data, page }`. Passe `page.nextCursor` como `cursor` enquanto
`page.hasMore` for `true`:

```typescript theme={null}
let cursor: string | undefined;
do {
  const response = await platform.customers.list({ projectId: "<project-id>", cursor });
  for (const customer of response.data.data) console.log(customer.id);
  cursor = response.data.page.hasMore ? response.data.page.nextCursor ?? undefined : undefined;
} while (cursor);
```

Veja [Paginação](/api/pagination) para os limites de página.


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