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, timeout acontece.

Rota Comportamento medido
POST /vasp/screenings Suporte completo: replay da decisão anterior, e 409 quando a mesma chave volta com corpo diferente
POST /transactions/analyze Deduplica repetição idêntica. Mesma chave com corpo diferente analisa a transação nova, sem 409
Emissão de relatório regulatório, laudo de KYC e fluxos DSAR Suporte com código IDEMPOTENCY_CONFLICT, e em parte dos casos a chave é obrigatória (IDEMPOTENCY_KEY_REQUIRED)
Demais rotas, inclusive POST /vasp/customers e POST /vasp/wallets Sem tratamento do header. A proteção contra duplicidade vem de chave única de negócio, como external_id ou o par transação e log da rede
Regra Detalhe medido
Tamanho Até 255 bytes. Não há mínimo imposto
Caracteres Caracteres de controle são recusados. Não há restrição a alfanumérico
Prefixo reservado Chave começando com snx-derived: é recusada: esse espaço pertence às chaves que o servidor deriva sozinho
Escopo Por tenant e por rota. A mesma chave em rotas diferentes não colide

Use UUID v4 ou um identificador derivado de evento de negócio (order_12345_attempt_1). Não use timestamp puro, porque duas tentativas legítimas em paralelo podem colidir.

Terminal window
curl -X POST https://api.crypto.sentinexrisk.com/api/v1/vasp/screenings \
-H "Idempotency-Key: txn_12345_attempt_1" \
-H "X-API-Key: $SENTINEX_API_KEY" \
-H "Content-Type: application/json" \
-d @payload.json

Repetição com a mesma chave e o mesmo corpo devolve a decisão original, marcada:

{
"success": true,
"idempotent_replay": true,
"data": { "screening_id": "", "decision": "REVIEW" }
}

Mesma chave com corpo diferente devolve 409:

{
"success": false,
"error": {
"code": "idempotency_key_conflict",
"message": "Idempotency-Key já usada com outro corpo."
}
}

Chave malformada devolve 400 com o código invalid_idempotency_key.

Dedução automática quando você não manda chave

Seção intitulada “Dedução automática quando você não manda chave”

Em POST /transactions/analyze, se você não enviar Idempotency-Key, o servidor tenta derivar uma a partir do identificador externo da operação mais o digest do corpo, dentro de uma janela de 10 minutos. Isso fecha a repetição causada por timeout de rede sem exigir header novo de ninguém.

Sem identificador externo e sem header, não há como distinguir uma repetição de uma segunda operação real, e a análise é feita de novo. Essa é a cláusula de escape declarada: ela não fecha o buraco, apenas se recusa a apagar uma operação que pode ser legítima.

Não existe expiração fixa de 24 horas para chave declarada por você. A deduplicação vale enquanto o registro correspondente existir na base. A janela de 10 minutos citada acima aplica-se somente à chave derivada pelo servidor.

import { v4 as uuid } from 'uuid';
async function screenWithRetry(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/screenings', 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, e o corpo também não. É isso que torna o retry seguro.

  • Não previne dois clientes criarem o mesmo recurso com chaves diferentes. Para isso use um identificador único de negócio (external_id)
  • Não substitui transação de banco: é dedup de chamada HTTP
  • Não atravessa tenants: chave válida no tenant A não interfere no tenant B
  • Não vale onde a rota não implementa o header. Confira a tabela do topo