Erros
Códigos HTTP, formato JSON consistente, lista de códigos comuns e como tratar com retry, fallback e logging.
A API retorna erros em um formato JSON consistente, com um código estável que você pode usar em código de retry e fallback — independente da mensagem (que pode mudar entre versões).
Formato
Todo erro vem assim:
{
"code": "validation_error",
"message": "Campo 'fullName' é obrigatório.",
"details": {
"field": "fullName"
}
}code— string estável. Nunca muda dentro de uma versão major.message— humano-legível, em PT-BR. Pode mudar.details— campos extras específicos do erro (opcional).
O payload de erro é flat — não há um campo error ao redor.
Sempre faça body.code, não body.error.code.
Códigos HTTP por categoria
| Status | Significado |
|---|---|
200 | Sucesso (GET, PATCH). |
201 | Criado com sucesso (POST). |
204 | Sucesso sem conteúdo (DELETE). |
400 | Requisição mal formada (JSON inválido, idempotency key). |
401 | Não autenticado (token ausente, inválido, revogado). |
403 | Autenticado mas sem permissão (scope insuficiente, plano). |
404 | Recurso não existe ou não pertence ao seu workspace. |
409 | Conflito (ex.: e-mail duplicado em cadastro de pessoa). |
422 | Validação semântica (regra de negócio violada). |
429 | Rate limit excedido. Veja o guia. |
500 | Erro interno. Reportável. |
Códigos de erro
A v1 expõe os seguintes códigos estáveis:
| Code | Status | Quando acontece |
|---|---|---|
unauthorized | 401 | Token ausente, malformado ou revogado. |
forbidden | 403 | Token válido mas sem permissão pra essa operação. |
plan_limit | 403 | Quota do plano atual atingida (ex.: número de pessoas). |
not_found | 404 | Recurso não existe ou está em outro workspace. |
conflict | 409 | Conflito de unicidade (ex.: e-mail já cadastrado). |
invalid_idempotency_key | 400 | Idempotency-key reutilizada com payload diferente. |
validation_error | 422 | Payload mal formado ou regra de negócio violada. |
request_error | 4xx | Erro genérico de requisição (4xx sem código mais específico). |
internal_error | 500 | Algo deu errado no servidor. Tente de novo. |
Padrão de retry recomendado
async function callApi(path: string, init: RequestInit, attempt = 1) {
const res = await fetch(`https://api.mixycrm.com.br/api/v1${path}`, init);
// Sucesso: retorna direto.
if (res.ok) return res.json();
// 429: respeita Retry-After.
if (res.status === 429) {
const retryAfter = Number(res.headers.get("retry-after") ?? 5);
await new Promise((r) => setTimeout(r, retryAfter * 1000));
return callApi(path, init, attempt + 1);
}
// 5xx: backoff exponencial até 5 tentativas.
if (res.status >= 500 && attempt < 5) {
const delay = Math.min(2 ** attempt * 1000, 30_000);
await new Promise((r) => setTimeout(r, delay));
return callApi(path, init, attempt + 1);
}
// 4xx (não 429): erro do cliente, não retentar.
const body = await res.json();
throw new ApiError(body); // body = { code, message, details? }
}Erros 400, 401, 403, 404, 409, 422 são deterministicamente do cliente. Retentar não muda o resultado e ainda gasta seu rate limit. Logue e investigue.
Logging recomendado
Em produção, logue estes campos pra facilitar debug:
code(chave de busca rápida).message.- Endpoint chamado, método, status code.
- Body da request (sem PII se possível) e do erro.
- Headers de rate limit (
x-ratelimit-*) — útil pra entender se você estava perto do limite.
Reportar um bug
Pra reportar comportamento inesperado da API, abra um chamado no app (ícone de suporte no canto inferior direito) com:
- Endpoint chamado e método.
- Body enviado (anonimize PII).
- Resposta recebida (
code+message). - Timestamp aproximado da chamada — facilita correlacionar com logs internos.
- O que você esperava acontecer.