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

# Erros

> Códigos HTTP, envelope padrão e padrão de error handling em 2 linguagens.

Toda resposta de erro da API Pontua usa o **mesmo envelope JSON**,
independente da rota que falhou:

```json theme={null}
{
  "service": "NomeDoServico",
  "method": "nomeDoMetodo",
  "message": "Descrição legível do erro"
}
```

| Campo     | Significado                                |
| --------- | ------------------------------------------ |
| `service` | Classe/serviço onde o erro foi originado   |
| `method`  | Método específico que falhou               |
| `message` | Mensagem em pt-BR pronta pra mostrar/logar |

O **status code HTTP** identifica a categoria do erro. A **mensagem** é
legível por humanos e tipicamente em português (pt-BR).

## Status codes principais

| Status                        | Significado                                            | Retry?                        |
| ----------------------------- | ------------------------------------------------------ | ----------------------------- |
| **200 OK**                    | Operação concluída, body com dados                     | —                             |
| **201 Created**               | Recurso criado, body com ID gerado                     | —                             |
| **204 No Content**            | Operação ok, sem body                                  | —                             |
| **400 Bad Request**           | Validação de schema falhou (campo errado, tipo errado) | ❌ Não — corrige o request     |
| **401 Unauthorized**          | Token ausente, inválido ou expirado                    | ❌ Não — corrige autenticação  |
| **403 Forbidden**             | Autenticado mas sem permissão                          | ❌ Não — verifica plano/escopo |
| **404 Not Found**             | Recurso não existe (ID inválido ou de outro tenant)    | ❌ Não — corrige ID            |
| **500 Internal Server Error** | Erro inesperado no backend                             | ✅ Sim — backoff + retry       |

Outras categorias HTTP (4xx específicas como 409, 422 etc.) podem
aparecer dependendo do endpoint. A `message` do envelope sempre indica
a causa concreta.

## Mensagens comuns

### `401 Unauthorized` — falhas de autenticação

```json theme={null}
{
  "service": "AuthCoreGuard",
  "method": "canActivate",
  "message": "Token inválido"
}
```

| Mensagem         | Causa típica                                                           |
| ---------------- | ---------------------------------------------------------------------- |
| `Token inválido` | Malformado, assinatura corrompida, ou de ambiente errado (PROD vs HML) |
| `Token expirado` | Atingiu o `exp` (expiração) — hora de rotacionar                       |

→ Detalhes em [Autenticação](/conceitos/autenticacao).

### `400 Bad Request` — validação de schema

Vem do `ValidationPipe` global do NestJS. Indica que o **payload tem
problema estrutural** (campo faltando, tipo errado, propriedade extra).

```json theme={null}
{
  "service": "ValidationPipe",
  "method": "transform",
  "message": [
    "nome should not be empty",
    "cpf must be a string"
  ]
}
```

A `message` pode ser um **array de strings** quando há múltiplos erros.
Trate defensivamente:

```javascript theme={null}
const msg = Array.isArray(erro.message) ? erro.message.join('; ') : erro.message
```

### `404 Not Found` — recurso inexistente

Atenção: se você está em [multi-tenant](/conceitos/multi-tenant), 404 pode
significar "ID existe, mas em outra UN". Cheque que o token pertence à UN
do recurso.

### `500 Internal Server Error` — erro interno

Indica algo inesperado no backend. Sempre transitório do ponto de vista
do cliente:

1. **Retente com backoff** (3-5 tentativas, jitter)
2. Se persistir, abra chamado em `tecnologia@pontua.com.br` com:
   * `service`, `method`, `message`
   * Timestamp da primeira ocorrência (BRT)
   * Payload exato que estava sendo enviado

## Estratégia de retry

```javascript theme={null}
const RETRIABLE_STATUSES = [500, 502, 503, 504]

async function comRetry(fn, maxAttempts = 5) {
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    try {
      return await fn()
    } catch (err) {
      const isRetriable = RETRIABLE_STATUSES.includes(err.status)
      const isLastAttempt = attempt === maxAttempts - 1

      if (!isRetriable || isLastAttempt) throw err

      // Exponential backoff: 1s, 2s, 4s, 8s, 16s + jitter
      const delay = Math.pow(2, attempt) * 1000 + Math.random() * 1000
      await new Promise((r) => setTimeout(r, delay))
    }
  }
}
```

## Error class wrapper (recomendado)

Encapsule o tratamento em uma classe específica para a Pontua:

<CodeGroup>
  ```javascript Node.js theme={null}
  class PontuaApiError extends Error {
    constructor(status, body) {
      const msg = Array.isArray(body.message) ? body.message.join('; ') : body.message
      super(`HTTP ${status} | ${body.service}.${body.method}: ${msg}`)
      this.name = 'PontuaApiError'
      this.status = status
      this.service = body.service
      this.method = body.method
      this.originalMessage = body.message
    }

    isRetriable() {
      return this.status >= 500
    }

    isAuthError() {
      return this.status === 401
    }
  }

  // Uso
  try {
    await pontua.criarColaborador(dados)
  } catch (e) {
    if (e instanceof PontuaApiError) {
      if (e.isAuthError()) await renovarToken()
      else if (e.isRetriable()) await comRetry(() => pontua.criarColaborador(dados))
      else throw e
    }
  }
  ```

  ```python Python theme={null}
  class PontuaApiError(Exception):
      def __init__(self, status: int, body: dict):
          msg = "; ".join(body["message"]) if isinstance(body["message"], list) else body["message"]
          super().__init__(f"HTTP {status} | {body['service']}.{body['method']}: {msg}")
          self.status = status
          self.service = body["service"]
          self.method = body["method"]
          self.original_message = body["message"]

      @property
      def is_retriable(self) -> bool:
          return self.status >= 500

      @property
      def is_auth_error(self) -> bool:
          return self.status == 401
  ```
</CodeGroup>

## Logging recomendado

Capture sempre os 3 campos no seu observability stack:

```javascript theme={null}
if (!resp.ok) {
  const erro = await resp.json()
  log.error({
    status: resp.status,
    service: erro.service,
    method: erro.method,
    message: erro.message,
    url: resp.url,
    timestamp: new Date().toISOString(),
  })
}
```

Isso permite agregar erros por `service.method` no Datadog/Sentry/CloudWatch
e identificar tendências.

## Veja também

* [Autenticação](/conceitos/autenticacao) — como evitar 401
* [Idempotência](/conceitos/idempotencia) — backoff exponencial detalhado
* [Versionamento](/conceitos/versionamento) — política de mudanças
