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

# Erros

> Formato padrão de erros e referência de todos os códigos de erro da API.

## Formato de erro

Todos os erros retornam um objeto JSON no seguinte formato:

```json theme={null}
{
  "error": {
    "type": "validation_error",
    "message": "Campo 'name' é obrigatório.",
    "code": "MISSING_REQUIRED_FIELD",
    "details": [
      {
        "field": "name",
        "message": "Campo obrigatório"
      }
    ]
  },
  "meta": {
    "request_id": "req_01jx8kz3m4n5p6q7r8s9t0u1v"
  }
}
```

| Campo             | Tipo   | Descrição                                      |
| ----------------- | ------ | ---------------------------------------------- |
| `error.type`      | string | Categoria do erro (ex: `validation_error`)     |
| `error.message`   | string | Mensagem legível para humanos                  |
| `error.code`      | string | Código de máquina para tratamento programático |
| `error.details`   | array? | Detalhes adicionais (ex: campos inválidos)     |
| `meta.request_id` | string | ID único da requisição para suporte            |

## Códigos HTTP

| Status | Tipo                   | Quando ocorre                              |
| ------ | ---------------------- | ------------------------------------------ |
| `200`  | Sucesso                | Leitura bem-sucedida                       |
| `201`  | Criado                 | Recurso criado com sucesso                 |
| `204`  | Sem conteúdo           | Exclusão bem-sucedida                      |
| `400`  | `validation_error`     | Corpo ou parâmetros inválidos              |
| `401`  | `unauthorized`         | API Key ausente ou inválida                |
| `403`  | `forbidden`            | Escopo insuficiente ou restrição de plano  |
| `404`  | `not_found`            | Recurso não encontrado                     |
| `409`  | `conflict`             | Conflito de dados (ex: duplicata)          |
| `422`  | `unprocessable_entity` | Dados válidos mas impossíveis de processar |
| `429`  | `rate_limit_exceeded`  | Limite de requisições atingido             |
| `500`  | `internal_error`       | Erro interno do servidor                   |

## Referência de códigos de erro

### Autenticação

| Código                   | Status | Descrição                               |
| ------------------------ | ------ | --------------------------------------- |
| `MISSING_API_KEY`        | `401`  | Header Authorization ausente            |
| `INVALID_API_KEY_FORMAT` | `401`  | Chave não começa com `sk_live_`         |
| `INVALID_API_KEY`        | `401`  | Chave inválida, expirada ou revogada    |
| `INSUFFICIENT_SCOPE`     | `403`  | Escopo necessário não concedido à chave |
| `PLAN_RESTRICTION`       | `403`  | Recurso não disponível no plano atual   |

### Validação

| Código                   | Status | Descrição                                     |
| ------------------------ | ------ | --------------------------------------------- |
| `INVALID_ID`             | `400`  | ID de recurso com formato inválido            |
| `MISSING_REQUIRED_FIELD` | `400`  | Campo obrigatório ausente                     |
| `INVALID_TYPE`           | `400`  | Valor de enum inválido                        |
| `INVALID_PRIORITY`       | `400`  | Prioridade inválida                           |
| `INVALID_CHANNEL`        | `400`  | Canal não suportado                           |
| `CHANNEL_NOT_SUPPORTED`  | `400`  | Canal não disponível na API pública           |
| `MISSING_CONTENT`        | `400`  | Conteúdo da mensagem ausente                  |
| `MISSING_MEDIA_URL`      | `400`  | URL de mídia ausente para mensagens não-texto |
| `MISSING_TAG`            | `400`  | Campo `tag` ausente                           |

### Recursos

| Código               | Status | Descrição                                 |
| -------------------- | ------ | ----------------------------------------- |
| `NOT_FOUND`          | `404`  | Recurso não encontrado na organização     |
| `PIPELINE_NOT_FOUND` | `404`  | Funil de venda não encontrado             |
| `STAGE_NOT_FOUND`    | `404`  | Etapa não encontrada no funil de venda    |
| `CONTACT_NOT_FOUND`  | `404`  | Contato não encontrado                    |
| `DUPLICATE_CONTACT`  | `409`  | Já existe contato com esse telefone/email |
| `DUPLICATE`          | `409`  | Registro duplicado                        |

### Processamento

| Código                 | Status | Descrição                            |
| ---------------------- | ------ | ------------------------------------ |
| `NO_WHATSAPP_JID`      | `422`  | Contato sem WhatsApp configurado     |
| `NO_WHATSAPP_INSTANCE` | `422`  | Nenhuma instância WhatsApp conectada |
| `NO_MESSENGER_PSID`    | `422`  | Contato sem PSID do Messenger        |
| `NO_MESSENGER_PAGE`    | `422`  | Nenhuma página Messenger conectada   |
| `NO_STAGES`            | `422`  | Funil de venda sem etapas            |
| `ALREADY_OPEN`         | `422`  | Negócio já está aberto               |
| `MESSENGER_SEND_ERROR` | `500`  | Erro ao enviar via Messenger API     |

### Rate Limiting

| Código                         | Status | Descrição                         |
| ------------------------------ | ------ | --------------------------------- |
| `RATE_LIMIT_EXCEEDED`          | `429`  | Limite por minuto atingido        |
| `RATE_LIMIT_BURST_EXCEEDED`    | `429`  | Limite de burst (10s) atingido    |
| `RATE_LIMIT_DAILY_EXCEEDED`    | `429`  | Limite diário atingido            |
| `WHATSAPP_INSTANCE_RATE_LIMIT` | `429`  | 30 msg/min por instância WhatsApp |
| `MESSENGER_PAGE_RATE_LIMIT`    | `429`  | 30 msg/min por página Messenger   |

## Tratando erros em código

<CodeGroup>
  ```javascript Node.js theme={null}
  async function apiRequest(url, options = {}) {
    const response = await fetch(`https://api.socialsell.ai/v1${url}`, {
      ...options,
      headers: {
        'Authorization': 'Bearer sk_live_...',
        'Content-Type': 'application/json',
        ...options.headers
      }
    });

    const data = await response.json();

    if (!response.ok) {
      const { error } = data;
      
      switch (error.code) {
        case 'RATE_LIMIT_EXCEEDED':
          const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
          // aguardar e tentar novamente
          await new Promise(r => setTimeout(r, retryAfter * 1000));
          return apiRequest(url, options);
          
        case 'DUPLICATE_CONTACT':
          // contato já existe — usar o ID retornado
          return { existing: error.existing_contact };
          
        default:
          throw new Error(`[${error.code}] ${error.message}`);
      }
    }

    return data;
  }
  ```

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

  def api_request(path, method='GET', **kwargs):
      url = f'https://api.socialsell.ai/v1{path}'
      headers = {'Authorization': 'Bearer sk_live_...'}
      
      response = requests.request(method, url, headers=headers, **kwargs)
      data = response.json()
      
      if not response.ok:
          error = data.get('error', {})
          code = error.get('code')
          
          if code == 'RATE_LIMIT_EXCEEDED':
              retry_after = int(response.headers.get('Retry-After', 60))
              time.sleep(retry_after)
              return api_request(path, method, **kwargs)
          
          raise Exception(f"[{code}] {error.get('message')}")
      
      return data
  ```
</CodeGroup>

## Header de Rate Limiting

Quando bem-sucedida, cada resposta inclui headers informativos:

```
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 2026-06-10T15:30:00.000Z
X-Request-Id: req_01jx8kz3m4n5p6q7r8s9t0u1v
```

Quando o limite é atingido, a resposta inclui:

```
Retry-After: 15
```

Aguarde o número de segundos indicado antes de tentar novamente.
