Skip to main content

Idempotency — retrying safely

Networks fail. A request can reach IDM, succeed, and the answer can still be lost on the way back. The Idempotency-Key header lets you retry without fear: the same intent is carried out once, however many times you send it.

The rule​

Create one key per user intent, at the moment the intent happens (the customer taps Book), and send that same key on every retry of that intent until you get a definitive answer. A new intent gets a new key.

Any unique string up to the allowed length works. A UUID is a good choice. Many teams build it from their own order ID, such as book-order-1042.

What IDM does with the key​

SituationWhat you get
First time IDM sees the keyThe request runs normally.
Same key, same request, still runningIDM waits up to 2 seconds for the first one to finish and returns its result. If it is still running, 409 REQUEST_IN_FLIGHT with Retry-After: 1. Nothing runs twice.
Same key, same request, already finishedThe original status code and body are replayed exactly, with the header Idempotency-Replayed: true. No new events, notifications or charges.
Same key, different request409 IDEMPOTENCY_KEY_REUSED. Nothing runs. You reused a key for a new intent; make a new one.
Missing key on an endpoint that requires one400 IDEMPOTENCY_KEY_REQUIRED.
The first attempt failed with a 5xxNot recorded, so your retry runs.

"Same request" means the same method, request URI and JSON body (key order and whitespace do not matter). Keys are remembered for a retention window set by Intellisol (a day at launch), and are scoped to your tenant and the operation.

Which endpoints need a key​

Every operation in the API reference shows its rule:

  • Required: confirming a quote into a job, Bulk deliveries, webhook registration and replay, cancel, reassign and complete.
  • Optional: quotes. With a key, a retry returns the same quote instead of a new one.
  • None: reads.

A safe retry loop​

async function withRetry(send, attempts = 4) {
for (let i = 0; i < attempts; i++) {
let res;
try {
res = await send(); // signs a fresh timestamp and nonce, reuses the idempotency key
} catch {
await sleep(2 ** i * 500); // network error: retry with the same key
continue;
}
if (res.status === 409) {
const err = await res.json();
if (err.code === 'REQUEST_IN_FLIGHT') { await sleep(1000); continue; }
return res; // a real conflict: read the resource and decide
}
if (res.status >= 500 || res.status === 429) {
await sleep(Number(res.headers.get('retry-after') ?? 2 ** i) * 1000);
continue;
}
return res;
}
throw new Error('IDM did not answer; the same key is safe to retry later');
}

Each retry needs a new nonce and timestamp for its signature, but the same idempotency key.

Business rules that also prevent duplicates​

The key is not the only protection. Even after a key has been forgotten:

  • A quote can be confirmed into one job only. Confirming it again returns that same job.
  • Your external_order_reference is unique among your live jobs. Reusing one is EXTERNAL_REFERENCE_IN_USE.
  • Cancelling a cancelled delivery returns it unchanged.