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.
Onde o header é honrado
Seção intitulada “Onde o header é honrado”| 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 |
Formato da chave
Seção intitulada “Formato da chave”| 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.
Como funciona em POST /vasp/screenings
Seção intitulada “Como funciona em POST /vasp/screenings”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.jsonRepetiçã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.
Janela de retenção
Seção intitulada “Janela de retenção”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.
Padrão recomendado
Seção intitulada “Padrão recomendado”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.
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. 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