Erros e retentativas
Formato da resposta de erro, política de retentativa por categoria HTTP e como usar request_id no suporte.
Quando algo falha na API da BlendFi, a resposta é sempre JSON e tem sempre o mesmo formato. Esta página mostra como ler, quais erros são seguros para retentar e como tratar request_id para suporte. Para a referência alfabética de cada code, veja o Catálogo de erros.
O formato da resposta de erro
{
"code": "kyc_required",
"message": "User KYC is not approved.",
"request_id": "01KPR9F6MM8G147177J7ZQPJHG",
"details": { /* opcional, presente em erros de validação */ }
}Decida pelo `code`
`code` é estável, legível por máquina e nunca muda de significado. Toda lógica de erro deve se basear nesse campo.
Mostre `message` para humanos
`message` é a descrição legível por humanos. Pode ser reescrita ao longo do tempo. Não faça `match` em cima do texto.
Envie `request_id` ao pedir ajuda
Toda resposta carrega um `request_id`. Inclua nos chamados, encontramos sua requisição em segundos.
Como tratar um erro
1. Faça parse do código primeiro
Não decida só pelo status HTTP. O mesmo 400 pode ser validation_error (você corrige), invalid_json (você também) ou idempotency_key_required (você esqueceu o cabeçalho). Bifurque pelo code.
const res = await fetch(url, options);
if (!res.ok) {
const error = await res.json();
switch (error.code) {
case "validation_error":
return showFieldErrors(error.details?.issues);
case "idempotency_key_required":
throw new Error("chave de idempotência ausente; bug na camada de retry");
case "rate_limit_exceeded":
return retryAfterBackoff(error);
case "internal_error":
return retryWithBackoff(error);
default:
return reportUnexpected(error);
}
}2. Decida se vale retentar
Use a política de retentativa abaixo. Versão curta: 5xx e 429 são sempre seguros para retentar com a mesma chave de idempotência. 4xx não, corrija a requisição e use uma chave nova.
3. Registre o request_id em tudo
Tendo a chamada dado certo ou errado, guarde o request_id (do corpo JSON) junto com a operação no seu log. Quando você reportar "a transação X tá presa", esse ID nos permite rastrear a requisição exata em segundos.
const body = await res.json();
log.info({
request_id: body.request_id,
transaction_id: body.id,
status: res.status,
}, "blendfi.transaction.created");Política de retentativa
A regra de uma linha
Retente 5xx e 429. Não retente 4xx. Use a mesma chave de idempotência ao retentar; gere uma chave nova só quando você tiver mudado a requisição para corrigir um 4xx.
| Faixa de status | Causa | Seguro retentar? | Como |
|---|---|---|---|
2xx | Sucesso | n/a | n/a |
400, 401, 403, 404, 409, 422 | Sua requisição | Não | Corrija e use uma chave de idempotência nova |
429 rate_limit_exceeded | Você está enviando rápido demais | Sim | Respeite o retry_after_seconds em details; backoff exponencial se omisso |
500 internal_error | Bug do lado da BlendFi | Sim | Mesma chave; backoff exponencial (250 ms, 500 ms, 1 s, 2 s, 4 s, então desiste) |
502, 503, 504 | BlendFi ou provedor externo indisponível | Sim | Mesma chave; backoff exponencial |
Um loop de retentativa razoável
async function withRetry(fn) {
const MAX_ATTEMPTS = 5;
let lastError;
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
try {
const res = await fn();
if (res.status < 500 && res.status !== 429) return res;
lastError = await res.json();
} catch (networkError) {
lastError = networkError;
}
const backoff = Math.min(2 ** attempt * 250, 4000);
const jitter = Math.random() * 100;
await sleep(backoff + jitter);
}
throw new Error("blendfi: retentativas esgotadas", { cause: lastError });
}Encapsule fn() para que a mesma chave de idempotência seja reaproveitada entre as tentativas. Veja Idempotência.
Códigos que toda integração precisa tratar
A lista completa está no Catálogo de erros. Os abaixo aparecem com mais frequência e merecem código dedicado:
| Código | HTTP | Onde costuma aparecer |
|---|---|---|
validation_error | 400 | Body, query ou parâmetros falharam validação. Detalhe em details.issues |
idempotency_key_required | 400 | Idempotency-Key ausente em POST ou PATCH |
idempotency_key_reused | 409 | Mesma chave com corpo diferente. Bug seu |
idempotency_key_in_progress | 409 | Tentativa anterior ainda rodando, espere |
authentication_failed | 401 | Chave desconhecida, revogada ou de ambiente errado |
missing_capability | 403 | Chave válida sem escopo para este endpoint |
kyc_required | 422 | KYC de plataforma do cliente final não está aprovado |
quote_expired | 409 | Cotação venceu (validade de 15 minutos para onramp) |
quote_already_consumed | 409 | Cotação já foi usada em uma transação ou conversão |
invalid_transaction_state | 409 | Tentou transição de estado proibida; releia o estado atual |
rate_limit_exceeded | 429 | Excedeu limite por chave neste endpoint |
internal_error | 500 | Falha do nosso lado, retentativa segura com backoff |
Códigos com sufixo _provider_unavailable (kyc_provider_unavailable, pix_provider_unavailable, etc.) sempre indicam falha transitória de upstream e são 502. Seguros para retentar.
Erros de validação em detalhe
Quando a resposta for 400 validation_error, details.issues lista cada campo com problema:
{
"code": "validation_error",
"message": "Request validation failed.",
"request_id": "01KPR9F6MM8G147177J7ZQPJHG",
"details": {
"issues": [
{ "path": "cpf", "message": "must be 11 digits" },
{ "path": "external_id", "message": "is required" }
]
}
}path é o caminho JSON do campo problemático. Mostre essas mensagens junto aos campos do seu formulário; o cliente final agradece.
FAQ
Devo retentar em um 400 validation_error?
Não. A mesma requisição falha do mesmo jeito toda vez. Corrija o corpo, gere uma chave nova e reenvie.
O code veio com um valor que eu nunca vi. O que faço?
Trate como internal_error para fins de retentativa: não retente operações destrutivas; retente leituras com backoff. Abra um chamado com o request_id e nós documentamos o código ou consertamos o bug.
Meu orçamento de retentativas acabou e não vi resposta final. Qual é o estado?
Para POST e PATCH, busque o recurso direto com GET usando os identificadores que você enviou. Se você usou external_id ou Idempotency-Key consistentes, o seu dado é encontrável; você só não sabe se o create original foi bem-sucedido, e a consulta esclarece.
429 não veio com retry_after_seconds. Quanto tempo eu espero?
Use backoff exponencial começando em 1 segundo, com teto de 30 segundos. Em respostas degradadas o details pode vir incompleto.
Qual a diferença entre authentication_failed e missing_capability?
authentication_failed (401) significa que não conseguimos identificar você (chave ruim, revogada, do ambiente errado). missing_capability (403) significa que sabemos quem você é, mas a sua chave não tem escopo para o endpoint. A primeira corrige copiando a chave certa; a segunda exige ajuste comercial.
Próximos passos
- Catálogo de erros: a lista alfabética com filtro, status, causa, recuperação.
- Idempotência: o contrato de retentativa, mesma chave, mesma resposta.
- Ambientes e limites: limites por chave e diferenças entre sandbox e produção.
Idempotência
Torne seus POSTs e PATCHes seguros para retentativas. Sem usuários duplicados, sem cobrança em dobro, mesmo quando a rede cai no meio da chamada.
Visão geral
Por que o Banco Central exige verificação de identidade, os dois níveis de KYC da BlendFi (plataforma e cripto), e como cada um afeta sua integração.
