Skip to main content
The Developer API accepts an Idempotency-Key request header on every mutating endpoint so you can safely retry after a network failure without double-creating, double-deducting, or double-updating.

Supported endpoints

MethodEndpointEffect of replay
POST/developer/vouchersNo second voucher
POST/developer/vouchers/{code}/redeemNo second deduction
POST/developer/customersNo second customer
PATCH/developer/customers/{customer_id}No second update
GET endpoints are naturally idempotent and ignore the header.

How it works

  1. Generate a unique key per logical operation (a UUID is fine). The header value can be up to 256 characters.
  2. Send the key in the Idempotency-Key header on the request.
  3. The server claims the key in Redis along with a SHA-256 fingerprint of the request body. The first request runs the operation and caches the response.
  4. Subsequent requests with the same key and the same body return the cached response verbatim – they do not re-execute the operation.
  5. The cache entry expires 24 hours after the first request.

Scoping

Idempotency keys are scoped to the API key that supplied them. Two different API keys can use the same idempotency key value without colliding, even within the same tenant.

Same key, different body → 409

If you reuse an Idempotency-Key with a different request body within the 24-hour window, the server returns 409 Conflict instead of silently replaying the original (now-stale) response:
HTTP/1.1 409 Conflict
Content-Type: application/json

{
  "detail": "Idempotency-Key was reused with a different request body. Use a fresh Idempotency-Key for a different operation."
}
This protects against the subtle bug where a client retries with a payload that has been mutated between attempts (a different amount, a different email, a different first_name) and would otherwise receive the original response with no indication that the new payload was discarded.

Concurrent duplicates → 409

If two requests with the same idempotency key arrive concurrently and the first has not yet finished, the second receives:
HTTP/1.1 409 Conflict
Content-Type: application/json

{
  "detail": "A duplicate request is already being processed. Please wait a moment and try again."
}
Wait briefly and retry – the cached response will then be available.

Endpoint errors release the key

If the first request fails (validation error, business-rule rejection, etc.) the pending claim is released so you can retry with the same Idempotency-Key after fixing the body. A transient endpoint error does not permanently burn an idempotency key for 24 hours.

Redis unavailability

If Redis is unreachable, idempotency degrades to fail-open: the operation runs, but the response is not cached. Retries in that window will execute again. This is rare and bounded – but design your client so a duplicate operation (a duplicate voucher, a duplicate redemption) produces a recoverable error rather than a silent double-write.

Example

curl -X POST \
  -H "X-API-Key: vg_live_EXAMPLEdoNotUseThis123456" \
  -H "Idempotency-Key: redeem-2026-04-26-VG-EXAMPLE1-001" \
  -H "Content-Type: application/json" \
  -d '{"amount": "25.00"}' \
  https://api.vouchergrid.com/api/v1/developer/vouchers/VG-EXAMPLE1/redeem
const idempotencyKey = crypto.randomUUID();

const res = await fetch(
  `https://api.vouchergrid.com/api/v1/developer/vouchers/VG-EXAMPLE1/redeem`,
  {
    method: "POST",
    headers: {
      "X-API-Key": process.env.VOUCHERGRID_API_KEY!,
      "Idempotency-Key": idempotencyKey,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ amount: "25.00" }),
  },
);