# Timeouts

> What happens when a request takes long: the bounds the server sets itself, what you receive when they are reached, and how to retry after a client-side timeout.

**Noryo publishes no latency commitment.** No value is promised until it has been measured on real traffic. Choose your client-side timeouts according to your own constraints, and expect a request to time out on your side occasionally.

## Server-side bounds

The server lets no step wait indefinitely. Each bound reached produces a precise response, and none of them bills:

| Step | If it exceeds its bound | Billed? | Memorised? |
|---|---|---|---|
| Resolution engine | `503 engine_error` | no | no |
| Waiting for an identical request in progress (same `Idempotency-Key`) | `503 busy`, with `Retry-After` | no | no |
| Contention at settlement | `503 busy`, with `Retry-After` | no | no |
| Settlement | `500 internal_error` | see below | see below |

The values of these bounds are internal settings, adjustable without changing the contract: they are not published and must not be used to size your timeouts.

## The `500` case

A `500` means Noryo could not confirm the outcome of the attempt. Since settlement is atomic (idempotency key, billing and response written together), only two cases are possible:

- the attempt was not settled: nothing exists, and a retry processes it as a first request;
- the attempt was settled: its response is memorised, and a retry with the same `Idempotency-Key` returns it, with `Noryo-Idempotency-Replayed: true`.

In both cases, **retrying with the same key gives the right response and bills only once**.

## After a client-side timeout

If your client gives up on a request before the response, you do not know whether it was settled. Do not guess: retry.

| What the request carried | Retry | Outcome |
|---|---|---|
| `Idempotency-Key` | Same key, same body | Memorised response if the first succeeded, otherwise normal processing. If the first is still in progress, the retry waits for it, or returns `503 busy`. |
| `external_transaction_id` only | Same body | If the first succeeded: its result, `deduplicated: true`, unbilled. Otherwise: normal processing. |
| Both | Same key, same body | Both protections apply. |

A create call with neither identifier is refused outright (`400 identifier_required`): there is no case in which a retry after a timeout could bill twice.

## Choosing a client-side timeout

- Set a per-request timeout and a maximum number of attempts, rather than an infinite overall timeout.
- Keep the same `Idempotency-Key` for every attempt of an operation.
- Measure your own latencies and log the `Noryo-Request-Id` of each attempt: in case of abnormal slowness, that is what lets support find the exact attempt.

A complete loop is given in [Retries](https://developer.noryo.app/en-GB/merchant/operations/retries).

---

Canonical page: https://developer.noryo.app/en-GB/merchant/operations/timeouts
