Idempotent retries
How to retry a create on the Notificado API without creating twice: the Idempotency-Key header, what a replay answers, and when to use a new key.
Updated:
A network can fail after the server has done the work: the case exists, but your program never saw the answer. Retrying blindly would create it twice. These operations accept an Idempotency-Key header so a retry is safe:
| Operation | Scope | Creates | A retry with the same key answers |
|---|---|---|---|
POST /v1/create-case |
cases:write |
a case | the same case |
POST /v1/add-party |
cases:write |
a party | the same party |
POST /v1/save-party-address |
cases:write |
an address in the party's address book | the same address |
POST /v1/request-upload |
cases:write |
an upload grant | the same grant: the same url and the same key |
POST /v1/confirm-upload |
cases:write |
a document with its SHA-256 | the same document |
POST /v1/request-import-upload |
cases:write |
an upload grant for a CSV | the same grant |
POST /v1/confirm-import-upload |
cases:write |
an import | the same import |
POST /v1/create-notification |
notifications:write |
a draft notification | the same draft |
POST /v1/attach-document |
notifications:write |
an attachment on the draft | the same attachment |
POST /v1/create-message-template |
notifications:write |
a message template | the same template |
POST /v1/send-notification |
notifications:write |
a send request for the lawyer | the same pending request: the same confirmUrl and the same expiresAt |
POST /v1/create-memorial |
notifications:write |
a memorial for the juzgado | the same memorial |
POST /v1/report-court-outcome |
notifications:write |
the juzgado's decision on the notification | the same record |
Each carries the Idempotency-Key badge in the reference. create-webhook-endpoint is left out on purpose: its answer carries the signing secret (whsec_…), which is handed over once and never stored for a replay. If that call fails on the network, list the webhooks with list-webhook-endpoints; if the new one is there, rotate its secret with rotate-webhook-secret instead of creating another.
The rule
One key per logical request. Generate a fresh random value (a UUID) when you decide to create something, and send that same key on every retry of that same request, with the same body.
KEY=$(uuidgen)
curl -X POST 'https://www.notificado.co/v1/create-case' \
-H "authorization: Bearer $NOTIFICADO_TOKEN" \
-H "idempotency-key: $KEY" \
-H 'content-type: application/json' \
-d '{"radicado":"05001310300320260012300","juzgado":"Juzgado 3 Civil del Circuito de Medellín","juzgadoEmail":"j03cctomed@cendoj.ramajudicial.gov.co","ciudad":"Medellín","especialidad":"Civil","claseProceso":"Verbal","cliente":"Inversiones Andinas S.A.S."}'
# timed out? run the same command again with the same $KEYconst key = crypto.randomUUID(); // once, before the first attempt
for (let attempt = 1; attempt <= 3; attempt += 1) {
try {
const response = await fetch('https://www.notificado.co/v1/create-notification', {
method: 'POST',
headers: {
authorization: `Bearer ${process.env.NOTIFICADO_TOKEN}`,
'content-type': 'application/json',
'idempotency-key': key,
},
body: JSON.stringify({
orgId: '<orgId>',
caseId: '<caseId>',
template: 'art8-personal',
providencia: 'Auto admisorio de la demanda',
providenciaFecha: '2026-09-21',
}),
});
if (response.status !== 429 && response.status < 500) return await response.json();
const wait = Number(response.headers.get('retry-after') ?? attempt * 2);
await new Promise((done) => setTimeout(done, wait * 1000));
} catch {
await new Promise((done) => setTimeout(done, attempt * 2000));
}
}What a retry answers
| Case | Answer |
|---|---|
| Same key, same body, first attempt finished | The first result, byte for byte, with x-ultimate-replayed: 1. Nothing is created again. |
| Same key, same body, first attempt still running | 409 X_IDEMPOTENCY_CONFLICT: wait and retry with the same key. A new key here would create twice. |
| Same key, different body | 409 X_IDEMPOTENCY_CONFLICT: a key names one request. Use a new key for a new request. |
| Same key, first attempt was refused | The same refusal, replayed. Fix the cause, then send a new key. |
| A key that is empty or longer than 255 characters | 400 X_IDEMPOTENCY_KEY_INVALID. |
A first answer carries x-ultimate-replayed: 0. Keys are remembered for 24 hours; after that the same key is a new request.
What it does not cover
- Operations outside the table ignore the header: reads, edits (
update-*), removals (remove-*,discard-draft) andcreate-webhook-endpoint. - Without a key, a repeat is a new request. A repeated
attach-documentanswersX_ATTACHMENT_DUPLICATE, which is safe to treat as done; a repeatedsend-notificationfiles another pending request (only one can ever send, but the lawyer sees two). - A key does not confirm a send:
send-notificationonly asks; the lawyer confirms in the panel within 24 hours. Repeating it with the same key afterawaiting_confirmationsends nothing and does not move the deadline. - A key does not replace reading the refusal:
X_CASE_RADICADO_TAKENmeans the case already exists, so look it up rather than retrying.
See also Errors and API authentication.