Erros
Entenda o envelope de erro padrão da Tokeflow, os tipos de erro, os códigos de status HTTP e como tratar falhas de forma confiável em produção.
A Tokeflow retorna erros em um único formato previsível em todos os endpoints. Construa seu tratamento de erros uma vez e ele funciona em qualquer lugar.
Toda requisição que falha retorna:
- Um código de status HTTP significativo (o sinal em nível de transporte).
- Um envelope de erro estruturado com um
typeecodelegíveis por máquina, umamessagelegível por humanos e umrequest_idpara o suporte.
Comece pelo type e pelo code nos seus fluxos de código, exiba a message para humanos e sempre registre o request_id em log.
Uma resposta bem-sucedida é encapsulada no envelope de sucesso (success, data, meta). Uma resposta que falha não é — ela tem um único objeto error no nível superior e nenhum campo success. Ramifique primeiro pelo código de status HTTP.
O envelope de erro
{
"error": {
"type": "validation_error",
"code": "INVALID_FIELD",
"message": "Validation failed",
"details": {
"amount": ["amount must be a positive integer"]
},
"request_id": "req_8f3c2a1b9d7e4f60",
"timestamp": "2026-01-15T12:30:00.000Z"
}
}| Field | Type | Sempre presente | Descrição |
|---|---|---|---|
type | string | Sim | Categoria de erro de alto nível. Um dos tipos de erro abaixo. Use isto para ramificar seu tratamento. |
code | string | Sim | Código estável e legível por máquina (por exemplo, INVALID_FIELD, RESOURCE_NOT_FOUND). Mais específico que type. Seguro para usar em um switch. |
message | string | Sim | Explicação legível por humanos. Seguro para exibir, mas o texto pode mudar com o tempo — nunca faça parsing dela. |
details | object | Não | Contexto opcional. Para erros de validação, um mapa de nome de campo para um array de mensagens. Pode ser omitido totalmente. |
decline_reason | string | Não | Presente apenas em erros de pagamento que tenham um: o nome que a Tokeflow dá ao motivo da falha do pagamento, o mesmo valor qualquer que seja o provedor que respondeu (por exemplo, invalid_cvv, insufficient_funds). Veja Motivos de recusa. |
request_id | string | Sim | Identificador único desta requisição (req_…). Registre-o em log e inclua-o em qualquer solicitação de suporte. |
timestamp | string | Sim | Horário em UTC no formato ISO 8601 em que o erro foi gerado. |
Use um switch em type para tratamento amplo (repetir a tentativa vs. corrigir a requisição) e em code quando precisar reagir a uma falha específica (por exemplo, IDEMPOTENCY_KEY_CONFLICT vs. RESOURCE_ALREADY_EXISTS, ambos sob conflict_error). Trate code como o contrato estável; trate message como apenas para exibição.
Como os erros fluem
Tipos de erro
Cada erro carrega um type que mapeia um-para-um com um código de status HTTP. Use o código de status para decidir sua estratégia; use type e code para decidir seu comportamento.
| Type | HTTP | Significado | Como tratar |
|---|---|---|---|
validation_error | 400 | O corpo da requisição ou os parâmetros de consulta falharam na validação. | Corrija a requisição. Inspecione details para mensagens em nível de campo. Não repita sem alterar. |
bad_request_error | 400 | A requisição estava malformada ou semanticamente inválida de um modo que não é validação de campo. | Corrija a requisição. Não repita sem alterar. |
authentication_error | 401 | A chave de API está ausente, malformada ou inválida. | Verifique o cabeçalho Authorization e a chave. Não repita sem alterar. |
authorization_error | 403 | A chave é válida, mas não tem o escopo necessário, ou o recurso alvo está fora do escopo da chave. | Use uma chave com o escopo certo. Não repita sem alterar. |
not_found_error | 404 | O recurso ou endpoint não existe (ou está fora do seu escopo). | Verifique o ID e o caminho. Não repita sem alterar. |
conflict_error | 409 | A requisição conflita com o estado atual (por exemplo, duplicata, reutilização de idempotency-key, recurso em uso). | Resolva o conflito e depois repita. Veja code para detalhes. |
business_rule_error | 422 | A requisição estava bem formada, mas viola uma regra de negócio (por exemplo, uma transição de estado inválida). | Corrija a operação para satisfazer a regra. Não repita sem alterar. |
rate_limit_error | 429 | Você excedeu o limite de taxa para esta chave de API. | Recue e repita com backoff exponencial. |
external_service_error | 502 | Um provedor downstream no caminho de orquestração retornou um erro ou estava inacessível. | Repita com backoff. Se persistir, inspecione o status do recurso. |
internal_server_error | 500 | Um erro inesperado ocorreu no lado da Tokeflow. | Repita com backoff. Se persistir, contate o suporte com o request_id. |
O campo code é mais granular que type. Por exemplo, um conflict_error pode carregar RESOURCE_ALREADY_EXISTS, DUPLICATE_ENTRY, RESOURCE_IN_USE ou IDEMPOTENCY_KEY_CONFLICT. Ramifique em type para a estratégia, em code para os detalhes.
Estratégia de repetição em resumo
- 4xx (exceto 429): corrija a requisição. O problema está na entrada — um campo inválido, um escopo ausente, um ID desconhecido ou uma violação de regra. Repetir a mesma requisição vai falhar do mesmo jeito.
- 429: recue. Você está sendo limitado por taxa. Repita com backoff exponencial e jitter.
- 5xx (
500,502): passível de repetição. Estes são transitórios. Repita de forma idempotente com backoff exponencial. Sempre envie umaIdempotency-Keyem mutações de criação/cobrança para que as repetições nunca dupliquem trabalho.
Nunca repita um 4xx que não seja 429 sem alterar — ele vai falhar de forma idêntica e desperdiçar seu orçamento de limite de taxa. Leia type, code e details, corrija a requisição e então tente novamente.
Exemplos de respostas de erro
400 — Erro de validação (validation_error)
Falhas em nível de campo aparecem sob details, indexadas por nome de campo. Cada valor é um array de mensagens.
{
"error": {
"type": "validation_error",
"code": "INVALID_FIELD",
"message": "Validation failed",
"details": {
"amount": ["amount must be a positive integer"],
"currency": ["currency must be a valid ISO 4217 code"]
},
"request_id": "req_8f3c2a1b9d7e4f60",
"timestamp": "2026-01-15T12:30:00.000Z"
}
}Os valores são inteiros em unidades menores (por exemplo, 15000 para R$150,00) e currency é ISO 4217. Veja Convenções para regras de dinheiro e formatação.
401 — Erro de autenticação (authentication_error)
A chave de API está ausente, malformada ou inválida.
{
"error": {
"type": "authentication_error",
"code": "INVALID_CREDENTIALS",
"message": "Authentication failed",
"request_id": "req_3a91c0ed7b224f18",
"timestamp": "2026-01-15T12:30:00.000Z"
}
}Chaves secretas (sk_…) são exclusivamente do lado do servidor e nunca devem aparecer em código de navegador. Envie-as no cabeçalho Authorization: Bearer <secret_key> a partir do seu backend. Veja Autenticação.
403 — Erro de autorização (authorization_error)
A chave é válida, mas não tem o escopo necessário, ou o recurso alvo está fora do escopo da chave.
{
"error": {
"type": "authorization_error",
"code": "INSUFFICIENT_PERMISSIONS",
"message": "This API key does not have the required scope: transactions:write",
"request_id": "req_77ba2c41e9f04d3a",
"timestamp": "2026-01-15T12:30:00.000Z"
}
}Os escopos seguem resource:action (por exemplo, transactions:read, products:write). Ao usar uma chave de Organization contra recursos com escopo de merchant, você também deve identificar o Merchant alvo por meio do parâmetro de consulta ou campo de corpo merchant_id. Veja Autenticação.
404 — Erro de não encontrado (not_found_error)
O recurso não existe, ou está fora do escopo da sua chave.
{
"error": {
"type": "not_found_error",
"code": "RESOURCE_NOT_FOUND",
"message": "Transaction not found",
"request_id": "req_5e0d8f3a6c124b97",
"timestamp": "2026-01-15T12:30:00.000Z"
}
}409 — Erro de conflito (conflict_error)
A requisição conflita com o estado atual. Uma causa comum é reenviar uma Idempotency-Key cujo corpo difere da requisição original.
{
"error": {
"type": "conflict_error",
"code": "IDEMPOTENCY_KEY_CONFLICT",
"message": "This idempotency key was already used with a different request body.",
"request_id": "req_1c47a98f2b6e40d5",
"timestamp": "2026-01-15T12:30:00.000Z"
}
}Reenviar a mesma Idempotency-Key com o mesmo corpo retorna o resultado original (HTTP 200 no reenvio vs. 201 na primeira criação) — não um conflito. Um conflito só ocorre quando a chave é reutilizada com um payload diferente. Veja Idempotência.
Outros códigos de conflito incluem RESOURCE_ALREADY_EXISTS (já existe um registro com esses valores), DUPLICATE_ENTRY e RESOURCE_IN_USE (o recurso é referenciado por outros registros e não pode ser excluído ou modificado).
422 — Erro de regra de negócio (business_rule_error)
A requisição estava bem formada, mas viola uma regra de negócio — por exemplo, tentar uma transição de estado inválida.
{
"error": {
"type": "business_rule_error",
"code": "INVALID_STATE_TRANSITION",
"message": "Cannot capture a transaction that has already been voided.",
"details": {
"current_status": "voided",
"requested_action": "capture"
},
"request_id": "req_9b2f6d1a3e8c47f0",
"timestamp": "2026-01-15T12:30:00.000Z"
}
}422 significa "Entendi a requisição, mas ela não pode ser aplicada no estado atual." Inspecione o status atual do recurso antes de repetir. Códigos relacionados incluem TRANSACTION_ALREADY_VOIDED, TRANSACTION_ALREADY_CAPTURED e TRANSACTION_ALREADY_REFUNDED.
429 — Erro de limite de taxa (rate_limit_error)
Você excedeu um limite de requisições para esta chave de API neste endpoint. Os limites são contabilizados por chave e por rota, em janelas de 60 segundos, e dependem da classe da rota — consulte Limites de taxa. A resposta traz Retry-After e, em details, retry_after_seconds e limit.
{
"error": {
"type": "rate_limit_error",
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Retry after a short delay.",
"request_id": "req_4d8e1a07f5c9426b",
"timestamp": "2026-01-15T12:30:00.000Z"
}
}Em 429, repita com backoff exponencial e jitter em vez de um atraso fixo. Distribuir as repetições evita um efeito de "manada" quando muitos clientes recuam ao mesmo tempo.
5xx — Erros de servidor e upstream (internal_server_error, external_service_error)
Um 500 indica um erro inesperado no lado da Tokeflow. Um 502 indica que um provedor downstream no caminho de orquestração falhou ou estava inacessível. Ambos são transitórios — repita com backoff.
{
"error": {
"type": "internal_server_error",
"code": "INTERNAL_ERROR",
"message": "Internal server error",
"request_id": "req_a1b2c3d4e5f60718",
"timestamp": "2026-01-15T12:30:00.000Z"
}
}Se um 5xx persistir após várias repetições com backoff, contate o suporte e inclua o request_id. Ele nos permite rastrear a requisição exata de ponta a ponta.
Tratando erros no código
Leia o status HTTP para decidir se deve repetir, e o envelope para decidir como reagir. O exemplo abaixo repete 429 e 5xx com backoff exponencial e expõe todo o resto para o chamador corrigir.
async function tokeflowRequest(url, options, attempt = 0) {
const MAX_RETRIES = 4;
const res = await fetch(url, options);
if (res.ok) {
return res.json();
}
const body = await res.json();
const { type, code, message, request_id } = body.error;
// Always log the request_id — it is your support handle.
console.error(`Tokeflow error ${res.status} ${code}: ${message} (request_id=${request_id})`);
const isRetryable = res.status === 429 || res.status >= 500;
if (isRetryable && attempt < MAX_RETRIES) {
// Exponential backoff with jitter: 0.5s, 1s, 2s, 4s (+/- jitter).
const delay = Math.pow(2, attempt) * 500 + Math.random() * 250;
await new Promise((r) => setTimeout(r, delay));
return tokeflowRequest(url, options, attempt + 1);
}
// 4xx (except 429): the request must change. Surface it to the caller.
const error = new Error(message);
Object.assign(error, { type, code, request_id, status: res.status });
throw error;
}# Inspect the status code and envelope on a failed request.
curl -i https://api.tokeflow.com/api/v1/transactions/tx_does_not_exist \
-H "Authorization: Bearer sk_live_mer_xxxxxxxxxxxxxxxx"
# HTTP/1.1 404 Not Found
# {
# "error": {
# "type": "not_found_error",
# "code": "RESOURCE_NOT_FOUND",
# "message": "Transaction not found",
# "request_id": "req_5e0d8f3a6c124b97",
# "timestamp": "2026-01-15T12:30:00.000Z"
# }
# }Boas práticas
- Registre o
request_idem log em toda falha. É a maneira mais rápida de o suporte rastrear uma requisição. Inclua-o em tickets e logs internos. - Ramifique em
typeecode, nunca emmessage. As mensagens são para humanos e podem mudar;typeecodesão o contrato estável. - Trate
5xxe429como passíveis de repetição com backoff exponencial e jitter; trate outros4xxcomo corrija-a-requisição. - Sempre envie uma
Idempotency-Keyem mutações de criação/cobrança para que repetições seguras nunca dupliquem trabalho. - Leia
detailsem400para identificar exatamente o campo que falhou na validação.
Relacionados
- Convenções — envelopes, paginação, idempotência, dinheiro e limites de taxa.
- Autenticação — chaves, escopos e cabeçalhos.
- Webhooks — verificação de assinatura e entrega de eventos.