Skip to content

Idempotency

Mutating requests can be made exactly-once by supplying an Idempotency-Key. A retry with the same key replays the original response instead of performing the mutation again — useful for safely retrying after a network error or timeout.

Using idempotency keys

Send an Idempotency-Key header (any unique string — a UUID is a good choice) on a mutating request:

bash
curl -X POST https://api.medialocker.io/api/buckets \
  -H "Authorization: Bearer <secret>" \
  -H "Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7" \
  -H "Content-Type: application/json" \
  -d '{"name": "campaign-assets"}'

Idempotency applies to mutating methods onlyPOST, PUT, DELETE, and PATCH. GET requests are naturally idempotent and ignore the header. Requests without the header are unaffected and incur no extra overhead.

How replays work

The first request with a given key runs normally and its successful response (any 2xx) is stored. A subsequent request with the same key, method, and URL replays the stored status code and body, with an extra header:

idempotency-replayed: true

Stored responses are kept for 24 hours, after which the key is forgotten and a new request with that key executes fresh.

Keys are scoped per principal, org, and URL

The cache key combines your Idempotency-Key with the caller (user or API key), the organization, and the full request URL including query string. The same key used by a different caller, a different org, or a different URL is treated as a distinct request — so keys never collide across tenants.

Reuse a key only for the same operation

Idempotency keys identify a specific operation. Reusing a key for a genuinely different request (different body but same method + URL) returns the original stored response, not a new result. Generate a fresh key per distinct operation.

Fail-open behavior

Idempotency is backed by Redis and fails open: if Redis is briefly unavailable, the request proceeds normally without idempotency rather than failing. Don't rely on idempotency as a correctness guarantee for non-retry-safe operations during an outage.

Excluded endpoints

The Stripe webhook (/api/stripe/webhook) never participates in idempotency — it is unauthenticated and Stripe provides its own at-least-once delivery with event-ID deduplication handled by the billing layer.

Released under the AGPL-3.0 License.