Idempotência
Numa integração que cria empréstimos e move dinheiro, um retry após um
timeout de rede não pode duplicar a operação. Para isso, os POST sensíveis
exigem o header Idempotency-Key.
Como funciona
Você gera uma chave única (ex.: um UUID) por operação lógica e a envia no header. Se a mesma chave chegar de novo, a API retorna a resposta original sem reexecutar nada — seguro para reenviar quantas vezes precisar.
A chave é isolada por credencial, método e rota: a mesma chave em endpoints diferentes são operações diferentes.
curl -X POST https://econsignado-api.socinal.com.br/v1/emprestimos \
-H 'Authorization: Bearer SEU_TOKEN' \
-H 'Idempotency-Key: 7b3e2c10-4f5a-4c8d-9a1b-2e6f0a9c1d34' \
-H 'Content-Type: application/json' \
-d '{ ... }'Na repetição, a resposta vem com o header X-Idempotency-Replayed: true.
Quais endpoints exigem
| Endpoint | Por quê |
|---|---|
POST /v1/emprestimos | Não duplicar emissão de CCB |
POST /v1/emprestimos/{id}/documentos | Não reprocessar pacote assinado |
POST /v1/emprestimos/{id}/documentos/{tipo} | Não reprocessar documento individual |
POST /v1/emprestimos/{id}/documentos-dataprev | Não reenviar os documentos ao Dataprev |
POST /v1/emprestimos/{id}/averbacao | Não averbar em duplicidade |
POST /v1/emprestimos/{id}/averbacao/retry | Não averbar em duplicidade na retentativa |
POST /v1/emprestimos/{id}/desembolso | Não duplicar PIX |
POST /v1/emprestimos/{id}/cancelamento | Não cancelar em duplicidade |
POST /v1/refinanciamento | Não duplicar a operação de refinanciamento |
POST /v1/renegociacao | Não duplicar a operação de renegociação |
Chamar esses endpoints sem o header resulta em 400 IDEMPOTENCY_KEY_OBRIGATORIA.
Chave reutilizada: replay, 409 ou reexecução?
O que acontece depende de como a primeira requisição terminou — e o corpo enviado agora também entra na conta:
| Situação | Resposta |
|---|---|
| A primeira já concluiu com sucesso (dentro da janela de 24h) | Replay: a resposta original, com o status original (ex.: 201) e X-Idempotency-Replayed: true. |
| A primeira ainda está em processamento | 409 CONFLITO_IDEMPOTENCIA (details.motivo: "em_processamento") — a chave está travada por uma requisição em voo. Tente de novo em instantes. |
A primeira falhou com 5xx | 409 CONFLITO_IDEMPOTENCIA (details.motivo: "tentativa_anterior_falhou") — veja abaixo. |
A primeira falhou com 4xx (pedido inválido) | Nada é gravado: o retry executa de verdade. Corrija o corpo e reenvie — pode reusar a mesma chave. |
| Mesma chave, corpo diferente | 409 CORPO_IDEMPOTENCIA_DIVERGENTE — nem replay nem reexecução. |
5xx: a chave fica travada de propósito
Um 5xx significa que não sabemos se a operação foi aplicada nos sistemas
externos (BaaS, Dataprev, D4Sign) antes do erro. Reexecutar às cegas duplicaria a
operação — ou devolveria um 422 enganoso de “proposta já inserida” no retry.
Por isso a chave é marcada como falha pendente de reconciliação e o retry recebe:
{
"error": {
"code": "CONFLITO_IDEMPOTENCIA",
"message": "A requisição anterior com esta Idempotency-Key falhou com erro interno (500) após possivelmente já ter aplicado o efeito da operação nos sistemas externos. ...",
"details": {
"motivo": "tentativa_anterior_falhou",
"efeito_upstream_possivel": true,
"status_falha": 500,
"falhou_em": "2026-01-15T12:34:56.789Z"
}
}
}O que fazer: consulte o recurso (GET /v1/emprestimos, GET /v1/propostas) ou
aguarde o webhook para descobrir se a operação foi criada. Se não foi, reenvie
com uma nova Idempotency-Key. Em caso de dúvida, acione o suporte — nunca
force o retry com a mesma chave.
Alguns 5xx provam que nada foi aplicado (ex.: OPERACAO_NAO_PROVISIONADA).
Nesses casos a chave continua retentável e a própria mensagem do erro diz para
repetir com a mesma Idempotency-Key.
Corpo divergente
O corpo da requisição é comparado por hash (a ordem das chaves do JSON não importa).
Reusar uma chave já registrada com um corpo diferente devolve
409 CORPO_IDEMPOTENCIA_DIVERGENTE — em vez de devolver silenciosamente a resposta
antiga sem criar a nova operação.
Uma chave por operação lógica. Uma chave representa uma única intenção: gere uma chave nova para cada nova operação e reuse a mesma chave apenas para retentar a mesma operação, com o mesmo corpo.
Boas práticas
- Use um UUID v4 por tentativa de operação e persista-o do seu lado antes de chamar — assim o retry reusa a mesma chave.
- A janela de deduplicação tem TTL (≈24h). Retries dentro desse período são seguros; depois disso, a chave pode não estar mais em cache.
- Combine idempotência com webhooks: mesmo que sua chamada dê timeout, o evento assíncrono confirma o resultado real.