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 a few narrow cases:

  • A request that provably never reached the implementing app: the connection was refused, DNS failed, or the dial timed out. This applies to any method.
  • A read (GET, HEAD, OPTIONS) that failed with a network error other than a timeout.
  • A route with a fallback implementation. If the app that normally serves it fails, the gateway sends the same request to the fallback, and the response carries X-Capability-Degraded: true.

Otherwise, once the app has received a write, the gateway does not retry it, because it can't know whether the write succeeded. A timeout is not retried either.

So a 502 or a timeout after the app was reached is handed back to you to decide on. On a route with a fallback, a write that timed out may have reached both implementations, which is one more reason to make writes safe to repeat.

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?