Pular para o conteúdo

Erros

A Public API usa HTTP status codes padrão + um campo error no body pra identificar o problema de forma programática.

Formato de erro

Toda resposta de erro tem este shape JSON:

{
"error": "<código_machine_readable>",
"message": "<descrição humana em pt-BR>",
"...campos contextuais específicos do erro"
}

Faça matching no error (estável, machine-readable). O message é amigável pra log/exibição mas pode mudar sem aviso.

Tabela de erros

400 — Bad Request

errorQuando
validation_failedBody inválido (campo obrigatório ausente, tipo errado, formato inválido de email/phone). Inclui details com lista de erros por campo
missing_required_fieldCampo obrigatório ausente (alternativa simplificada do anterior em alguns endpoints)
invalid_formatValor com formato incorreto (ex: data fora do ISO 8601, telefone sem código de país)

401 — Unauthorized

errorQuando
invalid_api_keyChave ausente, malformada, revogada ou expirada

403 — Forbidden

errorQuando
insufficient_scopeKey autenticou mas falta scope pra esse endpoint. Inclui required e have arrays
endpoint_not_exposedEndpoint não está disponível via API Key (só via UI/JWT)
tenant_mismatchTentativa de acessar recurso de outro tenant (não deveria acontecer com keys bem isoladas)

404 — Not Found

errorQuando
not_foundRecurso solicitado não existe ou foi deletado (soft-delete). Mensagem identifica qual recurso

409 — Conflict

errorQuando
duplicate_emailTentativa de criar contato com email já existente no tenant
duplicate_phoneTentativa de criar contato com telefone já existente
stage_invalidstageId informado não pertence ao pipeline informado

422 — Unprocessable Entity

errorQuando
business_rule_violationValidação passou no shape mas violou regra de negócio (ex: tentar mover deal pra stage de outro pipeline)

429 — Too Many Requests

errorQuando
rate_limit_exceededMais que N chamadas/minuto. Detalhes em Rate limiting →

500 — Internal Server Error

errorQuando
internal_errorErro inesperado no servidor. Reportado automaticamente — se persistir, abra um issue com o request id

Exemplos

Validação falhou

POST /api/v1/contacts
X-API-Key: crm_live_...
Content-Type: application/json
{
"firstName": "",
"email": "naoé-email"
}
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "validation_failed",
"message": "Validação dos campos falhou",
"details": [
{ "field": "firstName", "issue": "não pode ser vazio" },
{ "field": "email", "issue": "formato de email inválido" }
]
}

Scope insuficiente

{
"error": "insufficient_scope",
"message": "Scope insuficiente. Necessário: contacts:write",
"required": ["contacts:write"],
"have": ["contacts:read"]
}

Duplicate

{
"error": "duplicate_email",
"message": "Já existe um contato com este email no tenant",
"existingContactId": "ckl4z9w8v0001abcdef123456"
}

Use o existingContactId pra decidir: PATCH em vez de POST se a integração precisa do “upsert” idiomático.

Tratamento programático

const res = await fetch(url, opts)
if (!res.ok) {
const err = await res.json()
switch (err.error) {
case 'rate_limit_exceeded':
const wait = res.headers.get('retry-after') ?? 1
await new Promise(r => setTimeout(r, wait * 1000))
return retry()
case 'duplicate_email':
// Upsert: atualiza em vez de criar
return patch(err.existingContactId, payload)
case 'invalid_api_key':
case 'insufficient_scope':
throw new ConfigError(err.message) // alerta humano — config errada
default:
throw new Error(`API error ${err.error}: ${err.message}`)
}
}

Request ID

Toda resposta inclui o header X-Request-Id (UUID) — guarde em logs do seu lado. Se precisar reportar um bug, mandar esse ID acelera muito a investigação.