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

# Erros

> Formato das respostas de erro da API do Connecta DESK, tabela de códigos ERR_ e como tratar cada tipo de falha na sua integração.

Todo erro da API volta no mesmo formato, com um código fixo para a sua integração tratar e uma
mensagem em texto para as pessoas lerem.

```json theme={null}
{
  "error": {
    "code": "ERR_API_FORBIDDEN_SCOPE",
    "message": "O token não tem permissão de POST no módulo \"contacts\"."
  }
}
```

<ResponseField name="error.code" type="string" required>
  Identificador fixo do erro. Use este código para decidir o que a sua integração faz. O texto da
  mensagem pode mudar com o tempo, então não dependa dele.
</ResponseField>

<ResponseField name="error.message" type="string" required>
  Descrição em português, pensada para aparecer em log e em tela de suporte.
</ResponseField>

<ResponseField name="error.details" type="any">
  Informações extras, quando existem, como a lista de campos inválidos. A maioria dos erros não
  traz este campo.
</ResponseField>

<Warning>
  Trate qualquer resposta fora da faixa `2xx` como erro **antes** de ler `data`. Quando a chamada
  falha, `data` não existe, e ler `resposta.data.id` gera um `TypeError` que esconde a causa real.
</Warning>

## Códigos de autenticação e permissão

| Código | Status | O que fazer |
| - | - | - |
| `ERR_API_UNAUTHORIZED` | 401 | Token ausente, inválido, revogado ou expirado, ou IP não autorizado. A resposta é a mesma em todos esses casos. Confira o token no painel. |
| `ERR_API_FORBIDDEN_SCOPE` | 403 | O token não tem permissão para esse método nesse módulo. A mensagem diz qual é. Ajuste as permissões do token. |
| `ERR_API_NOT_ENABLED` | 403 | O plano da empresa não inclui a API pública. |
| `ERR_NO_PERMISSION` | 403 | A operação exige um nível de acesso que o token não tem. |
| `ERR_SELF_SERVICE_DISABLED` | 403 | Recurso disponível apenas em instalações self-service, como o convite de usuários. |

<Tip>
  Quando um `401` começa a aparecer de repente, a causa mais comum é o token ter expirado ou o IP
  de saída do seu servidor ter mudado. Confira a data de validade do token antes de investigar a
  rede.
</Tip>

## Códigos de requisição

| Código | Status | O que fazer |
| - | - | - |
| `ERR_API_VALIDATION` | 400 ou 422 | Falta um campo obrigatório ou algum valor não é aceito. A mensagem diz qual é o problema. |
| `ERR_API_INVALID_JSON` | 400 | O corpo não é um JSON válido. Confira aspas, vírgulas e o cabeçalho `Content-Type: application/json`. |
| `ERR_API_INVALID_PARAMETER` | 400 | Algum parâmetro tem formato inválido, como texto onde se espera número ou uma data que não existe. |
| `ERR_API_NOT_FOUND` | 404 | O registro não existe, ou o id informado não é um número. |
| `ERR_API_ROUTE_NOT_FOUND` | 404 | O método ou o caminho não existe. Confira o verbo e se a URL base inclui `/api/v1`. |
| `ERR_API_CONFLICT` | 409 | Já existe um registro com esses dados. |
| `ERR_API_INVALID_REFERENCE` | 422 | Um dos ids enviados no corpo não existe. |
| `ERR_API_PAYLOAD_TOO_LARGE` | 413 | O corpo passou do tamanho permitido. |
| `ERR_API_UPLOAD` | 413 ou 422 | O arquivo passou do tamanho permitido ou o envio não foi aceito. |
| `ERR_API_RATE_LIMIT` | 429 | A empresa atingiu o limite de requisições por minuto. Espere o tempo indicado em `Retry-After`. |
| `ERR_API_IDEMPOTENCY_KEY_INVALID` | 400 | `Idempotency-Key` com mais de 200 caracteres. |
| `ERR_API_IDEMPOTENCY_KEY_REUSED` | 409 | A mesma chave já foi usada com outro corpo. |
| `ERR_API_IDEMPOTENCY_IN_PROGRESS` | 409 | Outra tentativa com essa chave ainda está em andamento. |
| `ERR_API_INTERNAL` | 500 | Falha do nosso lado. Vale repetir depois de alguns segundos. Se continuar, fale com o suporte informando o método e o caminho. |

<Note>
  Um id de outra empresa responde `404`, exatamente como um id que não existe. É proposital: a API
  não confirma que um registro existe fora da sua conta.
</Note>

## Códigos de regra de negócio

Estes códigos aparecem quando a requisição está correta, mas a situação atual do recurso não
permite a operação.

<AccordionGroup>
  <Accordion title="Mensagens" icon="send">
    | Código | Status | Significado |
    | - | - | - |
    | `ERR_API_BODY_REQUIRED` | 400 | Mensagem do tipo `text` sem o campo `body`. |
    | `ERR_API_TEMPLATE_REQUIRED` | 400 | Mensagem do tipo `template` sem o campo `template`. Também aparece ao agendar envio em canal de WhatsApp oficial sem template. |
    | `ERR_API_INTERACTIVE_REQUIRED` | 400 | Mensagem do tipo `interactive` sem o campo `interactive`. |
    | `ERR_API_MEDIA_REQUIRED` | 400 | Mensagem do tipo `media` sem arquivo. |
    | `ERR_API_CHANNEL_NOT_FOUND` | 404 | O canal não existe nesta empresa. |
    | `ERR_API_TICKET_NOT_FOUND` | 404 | O atendimento informado em `ticketId` não existe nesta empresa. |
    | `ERR_API_JOB_NOT_FOUND` | 404 | O `jobId` não existe ou pertence a outra empresa. |
    | `ERR_API_TEMPLATE_MEDIA_NOT_ALLOWED` | 400 | O template informado não aceita mídia no cabeçalho. |
    | `ERR_API_CONTACT_NOT_IDENTIFIED` | 400 | Nenhum de `ticketId`, `contactId` ou `number`, ou só `number` num canal sem telefone. |
    | `ERR_API_CONTACT_NOT_FOUND` | 404 | O `contactId` não existe nesta empresa. |
    | `ERR_API_CONTACT_UNREACHABLE` | 422 | O contato não pode receber por este canal: sem conversa anterior nele (canais sem telefone; no Webchat, sem sessão do visitante neste webchat) ou sem telefone (canais de telefone). |
    | `ERR_API_INTERACTIVE_INVALID` | 422 | Webchat: o `interactive` está fora do formato ou dos limites do WhatsApp, ou é de um tipo que não existe no webchat. A `message` traz o campo e o motivo. |
    | `ERR_API_NO_WHATSAPP_CREDITS` | 402 | Canal WhatsApp Business com cobrança ativada e a empresa sem crédito. A mensagem não entra na fila. |
    | `ERR_API_ACTIVE_CONTACTS_LIMIT_REACHED` | 402 | A empresa atingiu o limite de contatos ativos do mês e o destinatário ainda não trocou mensagem com ela neste mês. Contatos que já contaram no mês seguem recebendo; a contagem zera no dia 1º. Não vale para canais `web_whatsapp`. A mensagem não entra na fila. |
  </Accordion>

  <Accordion title="Canais" icon="smartphone">
    | Código | Status | Significado |
    | - | - | - |
    | `ERR_API_CHANNEL_NOT_FOUND` | 404 | O canal não existe nesta empresa. |
  </Accordion>

  <Accordion title="Webchat" icon="messages-square">
    | Código | Status | Significado |
    | - | - | - |
    | `ERR_WEBCHAT_NOT_FOUND` | 404 | O `slug` não é de um webchat ativo desta empresa. |
    | `ERR_WEBCHAT_SESSION_NOT_FOUND` | 404 | A sessão não existe neste webchat. |
    | `ERR_WEBCHAT_SESSION_CLOSED` | 410 | A sessão foi encerrada. Abra outra (com `externalId`, a mesma é reaberta). |
    | `ERR_WEBCHAT_SESSION_BLOCKED` | 403 | A sessão está bloqueada por excesso de mensagens (1 hora). |
    | `ERR_WEBCHAT_VISITOR_DATA_REQUIRED` | 422 | Faltam dados iniciais obrigatórios do visitante. A `message` traz os campos (ex.: `name,phone`). |
    | `ERR_WEBCHAT_MESSAGE_EMPTY` | 422 | Mensagem sem texto e sem arquivo. |
    | `ERR_WEBCHAT_MESSAGE_TOO_LONG` | 422 | Texto acima de 4.096 caracteres. |
    | `ERR_WEBCHAT_MEDIA_TOO_LARGE` | 413 | Arquivo acima de 10 MB. |
    | `ERR_WEBCHAT_MEDIA_INVALID_TYPE` | 422 | Tipo de arquivo não aceito (o tipo é lido do conteúdo, não do nome). |
    | `ERR_WEBCHAT_UPLOAD_LIMIT` | 429 | Volume de arquivos do webchat na última hora esgotado. |
    | `ERR_WEBCHAT_INTERACTIVE_REPLY_INVALID` | 422 | `interactiveReply` com opção que não está na mensagem, ou mensagem que o visitante não vê. |
    | `ERR_WEBCHAT_LOCATION_INVALID` | 422 | `location` fora da faixa (latitude de -90 a 90, longitude de -180 a 180). |
  </Accordion>

  <Accordion title="Atendimentos" icon="message-square">
    | Código | Status | Significado |
    | - | - | - |
    | `ERR_NO_TICKET_FOUND` | 404 | O atendimento não existe nesta empresa. |
    | `ERR_API_TRANSFER_TARGET_REQUIRED` | 400 | Para transferir, informe a fila ou o usuário de destino. |
    | `ERR_API_TICKET_CLOSED` | 409 | `automation: true` em conversa encerrada que não tem mais o protocolo em aberto. |
    | `ERR_API_TICKET_IN_NPS` | 409 | `automation` em conversa que está na pesquisa de satisfação. |
    | `ERR_API_TICKET_IN_SERVICE` | 409 | `automation: true` em conversa com atendente responsável. Tire o atendente antes, com `POST /tickets/{id}/transfer` e `userId: null`. |
  </Accordion>

  <Accordion title="Tags" icon="tags">
    | Código | Status | Significado |
    | - | - | - |
    | `ERR_NO_TAG_FOUND` | 404 | A tag não existe ou foi apagada. |
  </Accordion>

  <Accordion title="Qualificações" icon="award">
    | Código | Status | Significado |
    | - | - | - |
    | `ERR_API_QUEUE_NOT_FOUND` | 422 | Alguma fila de `queues` não existe nesta empresa. |
  </Accordion>

  <Accordion title="Campanhas" icon="megaphone">
    | Código | Status | Significado |
    | - | - | - |
    | `ERR_NO_CAMPAIGN_FOUND` | 404 | A campanha não existe nesta empresa. |
  </Accordion>

  <Accordion title="CRM" icon="kanban">
    | Código | Status | Significado |
    | - | - | - |
    | `ERR_NO_BOARD_FOUND` | 404 | O painel não existe nesta empresa. |
    | `ERR_NO_BOARD_LIST_FOUND` | 404 | A etapa não existe no painel, ou a etapa de destino é de outro painel. |
    | `ERR_NO_CARD_FOUND` | 404 | O card não existe nesta empresa. |
    | `ERR_NO_CONTACT_FOUND` | 404 | O contato não existe nesta empresa. |
  </Accordion>

  <Accordion title="Relatórios" icon="chart-column">
    | Código | Status | Significado |
    | - | - | - |
    | `ERR_COMPANY_OR_DATE_NOT_DEFINED` | 400 | Faltou `date_start` ou `date_finish`. |
    | `ERR_API_VALIDATION` | 422 | A data não está no formato `AAAA-MM-DD` ou `AAAA-MM-DD HH:mm:ss`. |
  </Accordion>

  <Accordion title="Agendamentos" icon="calendar">
    | Código | Status | Significado |
    | - | - | - |
    | `ERR_API_SCHEDULE_ALREADY_SENT` | 409 | O agendamento já foi enviado e não pode ser alterado. |
    | `ERR_API_SCHEDULE_CANCELED` | 409 | O agendamento foi cancelado. |
    | `ERR_API_SCHEDULE_NOT_EDITABLE` | 409 | A situação atual do agendamento não permite edição. |
  </Accordion>

  <Accordion title="Usuários" icon="user-cog">
    | Código | Status | Significado |
    | - | - | - |
    | `ERR_API_USER_ID_REQUIRED` | 400 | A operação exige o id do usuário. |
    | `ERR_API_USER_NO_WRITABLE_FIELDS` | 400 | Nenhum campo alterável foi enviado. E-mail e senha pertencem à conta global do usuário e não podem ser editados pela API. |
    | `ERR_API_INVITE_ALREADY_ACCEPTED` | 409 | O convite já foi aceito e a pessoa já é membro da empresa. Para tirar o acesso dela, use `PUT /users/{id}/active`. |
    | `ERR_API_PROFILE_NOT_FOUND` | 422 | O `profileId` não é de um perfil desta empresa. Consulte os perfis em `GET /profiles`. |
  </Accordion>
</AccordionGroup>

## Como tratar cada tipo de erro

Cada faixa de status pede uma reação diferente. Um `400` volta igual se você repetir a chamada sem
mudar nada. Já um `429` deve ser repetido depois da espera indicada, senão a operação se perde.

<CodeGroup>
  ```javascript Node.js theme={null}
  async function tratar(res) {
    if (res.ok) return res.status === 204 ? null : (await res.json()).data;

    const { error } = await res.json().catch(() => ({ error: {} }));

    switch (res.status) {
      case 401:
        // Não repita: o token não vai melhorar sozinho. Alerte quem opera.
        throw new ErroDeCredencial(error.code, error.message);

      case 403:
      case 404:
      case 409:
      case 422:
        // Definitivos: repetir dá o mesmo resultado. Corrija a chamada.
        throw new ErroDeRequisicao(error.code, error.message, error.details);

      case 429:
        throw new ErroTemporario(Number(res.headers.get('Retry-After') ?? 60));

      default:
        // 5xx: falha do servidor. Vale retry com espera crescente.
        throw new ErroTemporario(5);
    }
  }
  ```

  ```python Python theme={null}
  def tratar(res):
      if res.ok:
          return None if res.status_code == 204 else res.json().get("data")

      erro = res.json().get("error", {}) if res.content else {}

      if res.status_code == 401:
          raise ErroDeCredencial(erro.get("code"), erro.get("message"))

      if res.status_code in (403, 404, 409, 422):
          raise ErroDeRequisicao(erro.get("code"), erro.get("message"), erro.get("details"))

      if res.status_code == 429:
          raise ErroTemporario(int(res.headers.get("Retry-After", 60)))

      raise ErroTemporario(5)
  ```
</CodeGroup>

<Note>
  Registre sempre o `error.code` no seu log, junto com o método e o caminho chamados. Com essas
  informações, o suporte consegue localizar a requisição, e tudo cabe em uma linha.
</Note>


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