Pular para o conteúdo

Erros e códigos

O contrato OpenAPI declara, em cada operação, os status de erro que ela devolve e, em cada status, as formas possíveis do corpo, com schema nomeado. Quando um status tem mais de uma forma, o contrato usa anyOf: o corpo casa com pelo menos uma delas. As formas se distinguem pelo tipo de error (texto ou objeto) e pela presença de code na raiz.

Schema no contrato error Código Quem responde assim
ErrorResponseCoded objeto error.code Chave de API, IP, ambiente e escopo da chave, limite da chave, rota não encontrada e parte dos erros de cadastro de clientes e carteiras
KYTPublicErrorResponse objeto error.code Análise de transação, consulta da análise, decisão do cliente e laudo KYT
VASPWalletErrorResponse objeto error.code Conflitos de carteira (409), com ids extras em error
ErroTexto texto nenhum Maior parte dos handlers de cadastro, webhooks, ledger e leituras regulatórias; falha inesperada (500); escudo de borda e limitadores indisponíveis (503); recusa por padrão de ataque (403)
ErroTextoComCodigo texto code na raiz Corpo acima de 1 MiB (413) e leitura do módulo contratado indisponível (503)
TenantAccessRefusalResponse texto code na raiz Conta sem direito comercial (402), conta que não pode usar a API (403), estado da conta indisponível (503)
ModuleNotEnabledResponse texto code na raiz Tenant sem o módulo de KYT contratado (403)
ErroLimite texto code às vezes Limitadores anteriores à operação (429), com retry_after quando há espera definida

É o formato da camada de chave de API e de parte da superfície /vasp/*.

{
"success": false,
"error": {
"code": "insufficient_scope",
"message": "API key missing required scope: vasp:read"
}
}

POST /vasp/screenings, GET /vasp/screenings/{id}, a decisão do cliente (PATCH /vasp/screenings/{id}/client-decision) e o laudo (POST /vasp/kyt-report) usam um objeto mais completo, que diz se vale repetir e em quanto tempo:

{
"success": false,
"error": {
"code": "KYT_PERSISTENCE_UNAVAILABLE",
"http_status": 503,
"message": "O screening não pôde ser consultado.",
"action": "Repita a requisição mais tarde.",
"retriable": true,
"retry_after_seconds": 30,
"request_id": "…"
}
}

retry_after_seconds e request_id só aparecem quando têm valor. Mesmo nessas operações, as camadas anteriores (chave, conta, limites, módulo) respondem nos próprios formatos.

{
"success": false,
"error": "Carteira não encontrada."
}

O código fica ao lado de error, e não dentro dele. A recusa da conta e a do módulo trazem details.

{
"success": false,
"code": "TENANT_SUSPENDED",
"error": "Sua conta está suspensa. Entre em contato com o suporte da Sentinex Risk."
}
{
"success": false,
"error": "Too many requests. Please try again later.",
"retry_after": 3600
}

O escudo de borda acrescenta code IP_BANNED, com reference para citar ao suporte, e os limites da busca de clientes e do laudo acrescentam detail. Os valores e a ordem dos limites estão em Limites de requisição.

Um consumidor defensivo faz assim:

function extrairCodigo(body) {
if (!body) return null;
if (typeof body.code === 'string') return body.code; // código na raiz
if (body.error && typeof body.error === 'object') { // objeto com código
return body.error.code ?? null;
}
return null; // texto simples
}

O identificador de correlação volta no header X-Request-ID da resposta, ecoando o que você enviou ou gerado pelo servidor. Só o envelope das operações de KYT o repete no corpo, em error.request_id. Nos demais formatos ele não vem no JSON: guarde o header.

O status é a parte estável do contrato. Ramifique por ele.

HTTP Quando acontece Ação recomendada
400 Corpo inválido, campo ausente, UUID malformado, enum fora do domínio Corrija o payload, não retente
401 Sem chave de API (missing_api_key) ou chave desconhecida, revogada ou expirada (invalid_api_key) Corrija a credencial
402 Conta sem direito comercial (TRIAL_EXPIRED) Fale com a Sentinex Risk. A requisição não foi processada
403 Chave recusada pelo IP, ambiente ou escopo; conta suspensa, não operacional ou com a API desligada; módulo de KYT não contratado; padrão de ataque na requisição Ajuste escopo, ambiente, IP ou contratação. Não retente
404 Recurso inexistente ou pertencente a outro tenant; com chave válida, caminho ou método que não corresponde a operação servida (route_not_found) Não retente
409 Conflito de estado, recurso duplicado, Idempotency-Key reutilizada com corpo diferente Releia o estado antes de decidir
413 Corpo acima de 1 MiB (BODY_TOO_LARGE) Reduza o corpo
422 Regra de negócio recusou a operação Leia a mensagem, corrija a intenção
429 Limite de chave, de IP, de hora ou da operação atingido Respeite Retry-After quando vier (ver Limites de requisição)
HTTP Quando acontece Ação recomendada
500 Falha inesperada. A mensagem pública é genérica de propósito, para não vazar interno Retry com backoff. Persistindo, abra ticket com o X-Request-ID
502 Provedor externo respondeu erro ao montar a evidência do laudo Retry com backoff, sem presumir fallback
503 Dependência indisponível: verificação da chave, armazenamento de contadores, estado da conta, leitura do módulo contratado, transação da requisição ou serviço da operação. Não presuma conclusão; reconcilie com a mesma chave de idempotência Retry com backoff; quando vier Retry-After: 1, a transação conflitou e pode ser repetida em seguida. Em decisão de risco, a plataforma prefere segurar a operação a aprovar sem dado
Camada Status Códigos
Chave de API 401, 403, 503 missing_api_key, invalid_api_key, api_key_ip_not_allowed, api_key_environment_required, api_key_environment_not_allowed, insufficient_scope, api_key_verification_unavailable (503: não foi possível verificar a chave; repita com backoff, não troque a credencial)
Limite da chave 429, 503 api_key_rate_limit_exceeded, rate_limit_unavailable, api_key_rate_limit_invalid
Conta 402, 403, 503 TRIAL_EXPIRED, TENANT_SUSPENDED, TENANT_NOT_OPERATIONAL, API_ACCESS_DISABLED, TENANT_ACCESS_DENIED, TENANT_ACCESS_STATE_UNAVAILABLE
Módulo contratado 403, 503 MODULE_NOT_ENABLED, MODULE_STORE_UNAVAILABLE
Escudo de borda 403, 413, 429 travessia de diretório na URL (403, sem código), BODY_TOO_LARGE, IP_BANNED
Rota 404 route_not_found
Análise, decisão e laudo 400 a 503 KYT_*, por exemplo KYT_REQUEST_INVALID, KYT_SCREENING_NOT_FOUND, KYT_IDEMPOTENCY_CONFLICT, KYT_INPUT_UNSCREENABLE, KYT_DEPENDENCY_UNAVAILABLE
Clientes e carteiras 400, 409, 422 invalid_customer, invalid_client_id, invalid_idempotency_key, invalid_customer_id, invalid_cursor, invalid_cpf_hash, invalid_cnpj_hash, invalid_risk_classification, invalid_kyc_tier, personal_filter_requires_post, invalid_search_body, invalid_wallet, invalid_wallet_id, invalid_mode, customer_already_exists, client_not_found, address_mismatch, wallet_already_exists, wallet_is_institutional, wallet_duplicate_legacy, wallet_has_open_process, wallet_address_active_elsewhere, KYT_IDEMPOTENCY_CONFLICT
Leituras regulatórias 503 regulatory_unavailable

A lista pode crescer: trate código desconhecido como erro genérico do status.

const RETRYABLE = new Set([408, 429, 500, 502, 503]);
async function callWithRetry(fn, maxAttempts = 4) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (err) {
const inProgress = err.status === 409 && err.body?.error?.code === 'KYT_IDEMPOTENCY_IN_PROGRESS';
if ((!RETRYABLE.has(err.status) && !inProgress) || attempt === maxAttempts) throw err;
const retryAfter = err.headers?.['retry-after'];
const delayMs = retryAfter
? parseInt(retryAfter, 10) * 1000
: (err.body?.error?.retry_after_seconds ?? Math.min(2 ** attempt, 30)) * 1000;
await new Promise(r => setTimeout(r, delayMs + Math.random() * 500));
}
}
}

Quando abrir ticket no suporte (dev-support@sentinexrisk.com), envie:

  1. O X-Request-ID da resposta de erro
  2. Timestamp da chamada (UTC)
  3. Endpoint mais método HTTP
  4. tenant_id (não envie payload com PII)

Prazo de suporte é o contratado, não um prazo inferido do código. Alertas de screening têm prazo operacional de 1 hora (CRITICAL) ou 4 horas (HIGH); isso não comprova resposta do suporte nem resolução automática.

HTTP 200 com data.fail_closed: true e recommendation: HOLD registra uma análise com evidência incompleta. Retenha e crie nova análise após recuperar a fonte, com nova chave. 503 KYT_IDENTITY_UNAVAILABLE também traz data.recommendation: HOLD, mas não cria análise nem cobra. Em ambos os casos, não libere por ausência de dado. Veja Como as regras decidem.