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,OPTIONSare safe. Retry them freely with backoff. - Writes —
POST,PUT,PATCH,DELETEare not automatically safe. If a write times out or returns502, 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
PUTsemantics where the capability supports them — setting a resource to a known state is naturally repeatable; blindPOSTappends 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
}