Retries & idempotency

Which requests are safe to retry, how the gateway retries on your behalf, and how to make your own writes safe to repeat.

Networks fail. The question on a failed request is always: is it safe to send again? This page is the answer for revenexx.

What's safe to retry

  • Reads — GET, HEAD, OPTIONS are safe. Retry them freely with backoff.
  • Writes — POST, PUT, PATCH, DELETE are not automatically safe. If a write times out or returns 502, the request may already have been applied — retrying it blindly can double-apply.

What the gateway already does

The gateway retries for you in one narrow, safe case: when a request provably never reached the implementing app (the connection was refused, DNS failed, or the dial timed out). Once the app has received the request, the gateway does not retry it — because it can't know whether the write succeeded.

So a 502 or a timeout after the app was reached is handed back to you to decide on.

Making writes safe to repeat

There is no Idempotency-Key header. Make writes idempotent at the data level instead:

  • Use a natural key. Create-or-update on a unique field (an order number, a SKU, an external id) so repeating the call converges on the same row rather than creating a duplicate.
  • Check before you write. For non-unique creates, look up by a client-supplied reference first, or have your App dedupe on one.
  • Prefer PUT semantics where the capability supports them — setting a resource to a known state is naturally repeatable; blind POST appends are not.
On a write that fails after it was sent (timeout or 502), don't retry automatically. Re-read the resource to see whether it landed, or rely on a natural key so a repeat is harmless.

A practical pattern

Idempotent create
// Converge on a stable identity instead of blind-creating.
async function upsertByCode(code, data) {
  const found = await getByCode(code)          // GET — safe to retry
  return found
    ? updateProduct(found.id, data)            // PUT/PATCH — converges
    : createProduct({ code, ...data })         // POST — guarded by the lookup
}
Was this page helpful?