BlendFi

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 statusCausaSeguro retentar?Como
2xxSucesson/an/a
400, 401, 403, 404, 409, 422Sua requisiçãoNãoCorrija e use uma chave de idempotência nova
429 rate_limit_exceededVocê está enviando rápido demaisSimRespeite o retry_after_seconds em details; backoff exponencial se omisso
500 internal_errorBug do lado da BlendFiSimMesma chave; backoff exponencial (250 ms, 500 ms, 1 s, 2 s, 4 s, então desiste)
502, 503, 504BlendFi ou provedor externo indisponívelSimMesma 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ódigoHTTPOnde costuma aparecer
validation_error400Body, query ou parâmetros falharam validação. Detalhe em details.issues
idempotency_key_required400Idempotency-Key ausente em POST ou PATCH
idempotency_key_reused409Mesma chave com corpo diferente. Bug seu
idempotency_key_in_progress409Tentativa anterior ainda rodando, espere
authentication_failed401Chave desconhecida, revogada ou de ambiente errado
missing_capability403Chave válida sem escopo para este endpoint
kyc_required422KYC de plataforma do cliente final não está aprovado
quote_expired409Cotação venceu (validade de 15 minutos para onramp)
quote_already_consumed409Cotação já foi usada em uma transação ou conversão
invalid_transaction_state409Tentou transição de estado proibida; releia o estado atual
rate_limit_exceeded429Excedeu limite por chave neste endpoint
internal_error500Falha 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

Nesta página