Idempotência
Idempotência permite retentar uma requisição não-idempotente (POST, PATCH) sem risco de criar recursos duplicados ou executar a mesma ação duas vezes. Essencial para integração robusta: rede falha, timeouts acontecem.
Quando usar
Seção intitulada “Quando usar”Use sempre que estiver criando ou modificando estado:
| Endpoint | Recomendado? |
|---|---|
POST /vasp/customers | ✅ Obrigatório em produção |
POST /vasp/deposits/crypto | ✅ Crítico: duplicar = creditar duas vezes |
POST /transactions/analyze | ✅ Evita cobrar 2x pelo scoring |
POST /alerts/:id/resolve | ✅ Evita registrar 2 resoluções |
GET /* | ❌ Já é idempotente por natureza |
PUT /* | ❌ Já é idempotente por definição REST |
Como funciona
Seção intitulada “Como funciona”Adicione o header Idempotency-Key em qualquer POST/PATCH:
curl -X POST https://api.crypto.sentinexrisk.com/api/v1/vasp/customers \ -H "Idempotency-Key: customer-onboarding-2026-05-18-uuid-v4" \ -H "X-API-Key: $SENTINEX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "external_id": "ACME-9012", "type": "PF", "document_hash": "<sha256-hex-do-cpf>" }'O servidor:
- Calcula
hash = SHA256(tenant_id + endpoint + idempotency_key) - Se hash já existe → retorna a resposta original (mesmo status, mesmo body)
- Se hash não existe → executa, persiste resposta, devolve
Janela de retenção
Seção intitulada “Janela de retenção”Chaves ficam válidas por 24 horas. Após isso são descartadas: uma retentativa com a mesma chave depois de 24h é tratada como request nova.
Conflito de payload
Seção intitulada “Conflito de payload”Se você enviar a mesma chave com payload diferente, recebe 409:
{ "success": false, "error": { "code": "IDEMPOTENCY_CONFLICT", "message": "Idempotency-Key 'customer-onboarding-2026-05-18-uuid-v4' já foi usada com payload diferente.", "details": { "key": "customer-onboarding-2026-05-18-uuid-v4", "first_seen_at": "2026-05-18T14:32:11Z" }, "request_id": "req_01HVZ…" }}Formato da chave
Seção intitulada “Formato da chave”| Regra | Detalhe |
|---|---|
| Tamanho | 8 a 128 caracteres |
| Charset | [a-zA-Z0-9_-] |
| Escopo | por (tenant_id, endpoint): você pode reusar a mesma chave em endpoints diferentes |
Padrão recomendado
Seção intitulada “Padrão recomendado”import { v4 as uuid } from 'uuid';
async function createCustomerWithRetry(payload) { const key = uuid(); // gerada UMA vez por intenção de negócio for (let attempt = 1; attempt <= 4; attempt++) { try { return await api.post('/vasp/customers', payload, { headers: { 'Idempotency-Key': key }, }); } catch (err) { if (!isRetryable(err) || attempt === 4) throw err; await sleep(2 ** attempt * 1000); } }}A chave não muda entre tentativas. É isso que torna o retry seguro.
O que NÃO é idempotência
Seção intitulada “O que NÃO é idempotência”- Não previne dois clientes criarem o mesmo recurso com chaves diferentes (use
external_idúnico para isso) - Não substitui transação de banco: é apenas dedup de chamadas HTTP
- Não preserva entre tenants: chave válida no tenant A não interfere no tenant B