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.
Idempotência
Uma chave de idempotência é como um post-it grudado na sua requisição. Se sua rede cai no meio de um POST /v1/transactions e você não sabe se a transação foi criada, você tenta de novo com a mesma chave — e a BlendFi devolve a mesma resposta em vez de criar uma segunda transação. Sem duplicatas. Sem cobrança em dobro.
Todo POST e PATCH na API da BlendFi exige um cabeçalho Idempotency-Key. Este guia mostra como usar do jeito certo.
Resumo
Uma chave por operação lógica
Gere um UUID novo por intenção (uma cotação, uma criação de usuário, uma transação). Reuse a mesma chave nas retentativas dessa intenção.
Mesma chave nas retentativas, mesma resposta
Mesma chave + mesmo corpo retorna a resposta original — mesmo status, mesmo JSON. A operação nunca é executada duas vezes.
Corpo diferente com a mesma chave é rejeitado
Se você reusa uma chave para uma requisição diferente sem querer, recebe `409 idempotency_key_reused`. Isso protege você dos seus próprios bugs.
Como funciona
1. Gere uma chave por intenção
Crie uma string única antes de enviar a requisição. UUID v4 é o formato recomendado — qualquer coisa aleatória e imprevisível serve.
import { randomUUID } from "node:crypto";
const idempotencyKey = randomUUID();
// "0196c5d9-2e34-7c24-a47e-a0e1f89bb8a9"A chave representa uma operação lógica, não uma tentativa de rede. Se você tentar de novo, reuse a mesma chave.
2. Envie a chave em todo POST e PATCH
Adicione no cabeçalho Idempotency-Key em toda requisição que escreve dado:
curl -X POST $BLENDFI_BASE/v1/transactions \
-H "Authorization: Bearer $BLENDFI_KEY" \
-H "Idempotency-Key: 0196c5d9-2e34-7c24-a47e-a0e1f89bb8a9" \
-H "content-type: application/json" \
-d '{"quote_id": "01J..."}'Se você esquecer o cabeçalho num POST ou PATCH, a BlendFi rejeita com 400 idempotency_key_required. GET, DELETE e outros métodos somente leitura não precisam.
3. Tente de novo com a mesma chave em caso de falha
Quando uma requisição estoura o tempo, a conexão cai ou você recebe um 5xx — tente de novo com a mesma chave e o mesmo corpo. A BlendFi vai:
- Se a requisição original deu certo, devolve a mesma resposta (mesmo status, mesmo corpo) sem reexecutar.
- Se a requisição original ainda estiver em andamento (menos de 60 segundos), retorna
409 idempotency_key_in_progress. Espere e tente de novo. - Se a requisição original falhou com
5xx, reexecuta a operação. Resultados5xxnunca são cacheados, porque a falha significa que a gente não sabe se a operação aconteceu de fato.
async function createTransaction(quoteId, key) {
for (let attempt = 0; attempt < 4; attempt++) {
const res = await fetch(`${BASE}/v1/transactions`, {
method: "POST",
headers: {
Authorization: `Bearer ${KEY}`,
"Idempotency-Key": key,
"content-type": "application/json",
},
body: JSON.stringify({ quote_id: quoteId }),
});
if (res.status >= 500) {
await sleep(2 ** attempt * 250);
continue;
}
return res.json();
}
throw new Error("transaction failed after retries");
}O que se mantém igual entre retentativas
Quando a BlendFi reexibe uma resposta armazenada, tudo bate com a original:
| Primeira chamada | Chamada repetida (mesma chave) | |
|---|---|---|
| Status HTTP | 201 Created | 201 Created |
| Corpo da resposta | {"id":"01J...","status":"pending",...} | {"id":"01J...","status":"pending",...} (byte por byte) |
| Efeitos colaterais | Transação criada, evento de auditoria emitido | Nenhum — sem segunda transação, sem segundo evento de auditoria |
| Cabeçalhos | Originais | Originais (exceto content-length, recalculado) |
Isso significa que sua lógica de retentativa pode tratar uma resposta repetida como se fosse a original. Você não precisa detectar "essa é uma repetição" — só fazer parse.
Regras de retentativa em uma lista
A regra do 4xx vs 5xx
Erros na faixa 4xx (falhas de validação, conflitos, capacidades faltando) são cacheados. Tentar de novo com a mesma chave devolve o mesmo 4xx — corrija a requisição e use uma chave nova.
Erros na faixa 5xx (falhas do lado da BlendFi) não são cacheados. Tentar de novo com a mesma chave reexecuta a operação. Isso é intencional: um 5xx quer dizer que a gente não sabe se a operação deu certo, então mantemos a chave ativa para retentativas.
| Resultado original | O que acontece na retentativa com a mesma chave |
|---|---|
Sucesso 2xx | Devolve a resposta cacheada. Sem reexecução. |
Erro de cliente 4xx | Devolve o erro cacheado. Use chave nova após corrigir. |
Erro do servidor 5xx | Reexecuta. Retentativa é segura. |
| Ainda em andamento (até 60 s) | Retorna 409 idempotency_key_in_progress. Espere. |
| Corpo diferente, mesma chave | Retorna 409 idempotency_key_reused. Bug — investigue. |
Armadilhas comuns
Reusar uma chave em várias operações.
Uma chave está atrelada a uma requisição lógica — um POST /v1/users, um POST /v1/transactions, etc. Se você reusa uma chave de criação de usuário numa criação de transação, recebe 409 idempotency_key_reused porque o corpo é diferente.
Gerar uma chave nova por tentativa em vez de por intenção.
Se seu loop de retentativa chama randomUUID() dentro do loop, cada retentativa recebe uma chave diferente, e a BlendFi trata cada uma como uma requisição nova. Você vai criar duplicatas. Gere a chave uma vez, fora do loop.
Descartar chaves antes de tentar de novo em fluxos longos. O sandbox guarda registros de idempotência por 24 horas. Se sua retentativa acontecer mais de 24 horas depois, a chave já expirou e a requisição executa do zero. Para fluxos one-shot (criação de usuário, execução de transação) isso não é problema. Para fluxos de vários dias, persista a resposta após a primeira chamada bem-sucedida para não precisar tentar de novo depois da expiração.
Tratar conflito em andamento como erro fatal.
409 idempotency_key_in_progress significa que uma tentativa anterior ainda está rodando. Espere um segundo e tente de novo — não dê erro fatal.
Quando você não precisa
Requisições somente leitura (GET, HEAD, OPTIONS) não precisam de chave de idempotência. Não têm efeito colateral para deduplicar. O cabeçalho só é rejeitado nesses métodos se você passar com valores conflitantes explicitamente.
DELETE é naturalmente idempotente — deletar um recurso já deletado retorna 404, não um delete duplicado. Por isso a gente não exige chave de idempotência em DELETE.
O que ler em seguida
Erros e retentativas
A política completa de retentativa por código de erro — quais valores de `code` são seguros para retentar, e com qual backoff.
Ciclo de vida da transação
Veja a idempotência em contexto: como retentativas em cada passo afetam o estado da transação.
Referência da API
Cada endpoint POST e PATCH — todos eles aceitam o cabeçalho Idempotency-Key.
FAQ
Que formato a chave precisa ter? Qualquer string entre 1 e 255 caracteres. UUID v4 é o recomendado. Não use inteiros sequenciais — colisões entre sistemas viram um risco real.
Por quanto tempo a BlendFi lembra de uma chave? 24 horas a partir do primeiro uso, por organização. Depois disso, a chave é esquecida e uma requisição nova executa do zero se você tentar de novo.
Duas organizações diferentes podem usar a mesma chave?
Podem. Os registros de idempotência são separados por organização, então a idempotency-key: 1234 do parceiro A é independente da do parceiro B. Não tem risco de colisão entre tenants.
E se minha retentativa atingir uma região diferente da BlendFi? O store de idempotência da BlendFi é centralizado — todas as regiões consultam o mesmo conjunto de registros. Você não vai ter execução duplicada por failover regional.
A verificação de idempotência confere o corpo da requisição byte por byte?
A gente faz hash de método + caminho + corpo com SHA-256 e compara. Qualquer mudança em qualquer um dos três dispara 409 idempotency_key_reused. Espaços em branco e ordem das chaves no JSON importam — se você serializar diferente na retentativa, vai bater no check.
E se eu quiser forçar uma reexecução depois de uma requisição que deu certo? Use uma chave nova. Não tem API para invalidar uma resposta armazenada.
