# Headers

> Every request and response header of Noryo Merchant API, its value, and on which responses it is present.

HTTP header names are case-insensitive: the API writes them in lower case in its responses (`noryo-request-id`), the documentation writes them capitalised (`Noryo-Request-Id`). It is the same header.

## Request headers

| Header | Routes | Required | Value |
|---|---|---|---|
| `Authorization` | all | yes | `Bearer nry_mer_<env>_<key_id>.<secret>`. See [Authentication](https://developer.noryo.app/en-GB/merchant/authentication). |
| `Content-Type` | `POST` | yes | `application/json`. A parameter such as `; charset=utf-8` is accepted. Any other type returns `415`. |
| `Idempotency-Key` | `POST /merchant/v1/enrichments` | no, but see below | 1 to 255 bytes of visible ASCII (`0x21` to `0x7E`), with no normalisation. |
| `Idempotency-Key` | `POST …/refresh` | **yes** | Same format. Its absence returns `400 identifier_required`. |

Details:

- On create, the `Idempotency-Key` is optional provided the body carries an `external_transaction_id`; with neither, the request is refused with `400 identifier_required`. See [The two identifiers](https://developer.noryo.app/en-GB/merchant/identifiers).
- An `Idempotency-Key` header that is present but empty, too long, contains a space or a character outside visible ASCII, or is repeated, is refused with `400 invalid_idempotency_key`. Its shape is checked on every route, including `GET`, where it has no effect.
- A `Noryo-Request-Id` sent by the client is ignored and replaced: you do not choose the Noryo identity of your request.

## Response headers

| Header | Present on | Value |
|---|---|---|
| `Noryo-Request-Id` | every response | `req_<uuid>`, the identifier of this attempt. Identical to the `request_id` in an error body. See [Request IDs](https://developer.noryo.app/en-GB/merchant/operations/request-ids). |
| `Content-Type` | every response | `application/json` for a success, `application/problem+json` for an error |
| `Cache-Control` | every response | `no-store` |
| `Noryo-Idempotency-Replayed` | `200` of both `POST` routes | `true` if the response comes from the idempotency memory, `false` otherwise |
| `Noryo-RateLimit-Limit` | `200` of all three routes, and `429` | The burst capacity of your key's bucket |
| `Noryo-RateLimit-Remaining` | `200` of all three routes, and `429` | The tokens left in your bucket |
| `Retry-After` | `429`, `503 busy`, `503 pricing_not_activated` | A whole number of seconds to wait |
| `Location` | `200` of the refresh | The **relative** path of the new enrichment, for example `/merchant/v1/enrichments/enr_dd424f15-…` |
| `WWW-Authenticate` | `401`, `403 insufficient_scope` | `Bearer realm="noryo-merchant-api", error="invalid_token"` or `error="insufficient_scope"` |
| `X-Content-Type-Options` | every response | `nosniff` |
| `Referrer-Policy` | every response | `no-referrer` |
| `X-Robots-Tag` | every response | `noindex, nofollow` |

> **Note** Error responses other than `429` do not carry the `Noryo-RateLimit-*` headers, even when they consumed a token. To follow your balance, read them on your `200` responses.

## `Location` is relative

The refresh announces the new `enrichment_id` with a relative path, never an absolute URL: no hostname enters the contract. Resolve it against your configured base URL.

## `Retry-After` is only set where retrying makes sense

A `Retry-After` only appears on refusals that a retry of the same request can clear: the rate limit, transient contention (`busy`), and billing not yet activated (`pricing_not_activated`). It never appears on a `400` or a `409`. See [Retries](https://developer.noryo.app/en-GB/merchant/operations/retries).

---

Canonical page: https://developer.noryo.app/en-GB/merchant/requests/headers
