Skip to content

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 $KEY
const 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) and create-webhook-endpoint.
  • Without a key, a repeat is a new request. A repeated attach-document answers X_ATTACHMENT_DUPLICATE, which is safe to treat as done; a repeated send-notification files another pending request (only one can ever send, but the lawyer sees two).
  • A key does not confirm a send: send-notification only asks; the lawyer confirms in the panel within 24 hours. Repeating it with the same key after awaiting_confirmation sends nothing and does not move the deadline.
  • A key does not replace reading the refusal: X_CASE_RADICADO_TAKEN means the case already exists, so look it up rather than retrying.

See also Errors and API authentication.