Pular para o conteúdo

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.

Use sempre que estiver criando ou modificando estado:

EndpointRecomendado?
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

Adicione o header Idempotency-Key em qualquer POST/PATCH:

Terminal window
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:

  1. Calcula hash = SHA256(tenant_id + endpoint + idempotency_key)
  2. Se hash já existe → retorna a resposta original (mesmo status, mesmo body)
  3. Se hash não existe → executa, persiste resposta, devolve

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.

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…"
}
}
RegraDetalhe
Tamanho8 a 128 caracteres
Charset[a-zA-Z0-9_-]
Escopopor (tenant_id, endpoint): você pode reusar a mesma chave em endpoints diferentes
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.

  • 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