# Authentication

> How to present an API key, what the server checks, why every refused key gets the same 401, and which scopes open which routes.

Every request authenticates with an API key sent in the `Authorization` header, using the `Bearer` scheme:

```http
Authorization: Bearer nry_mer_sbx_<key_id>.<secret>
```

There is no other way to authenticate: no URL parameter, no body field, no token exchange.

## The shape of the header

The server reads the header strictly:

- the scheme name is case-insensitive (`Bearer`, `bearer`, `BEARER`);
- exactly one space separates the scheme from the key;
- no space, tab or line break before, after or inside the key;
- no other scheme is accepted.

Any other shape is refused with `401`. The server corrects nothing, trims nothing and does not change case.

## The shape of the key

| Part | Value |
|---|---|
| Prefix | `nry_mer_sbx_` (Sandbox) or `nry_mer_live_` (Production) |
| `key_id` | 10 characters from the alphabet `23456789ABCDEFGHJKMNPQRSTVWXYZ`, upper case |
| Separator | one dot `.` |
| Secret | Base64URL without padding; a key issued by Noryo carries a 43-character secret |

The prefix announces the environment, but it is never the authority: the server confirms the environment of the registered key and refuses a key whose declared prefix does not match. A key for another Noryo API is refused on this one, even when perfectly formed. See [API keys](https://developer.noryo.app/en-GB/merchant/authentication/api-keys).

## One code for every refused key

Missing, malformed, unknown, revoked, wrong environment prefix: the response is **always** the same.

```http
HTTP/1.1 401 Unauthorized
content-type: application/problem+json
www-authenticate: Bearer realm="noryo-merchant-api", error="invalid_token"
noryo-request-id: req_…
```

```json
{
  "type": "https://developer.noryo.app/errors/invalid_api_key",
  "title": "Invalid API credential",
  "status": 401,
  "code": "invalid_api_key",
  "detail": "The API credential is missing, malformed, revoked, or unknown.",
  "request_id": "req_…"
}
```

Any distinction would tell an attacker which half of a guess was right. A `401` consumes no rate-limit token. See [invalid_api_key](https://developer.noryo.app/en-GB/errors/invalid_api_key).

## Scopes

A key carries scopes, chosen when it is created.

| Scope | Routes |
|---|---|
| `enrich:read` | `GET /merchant/v1/enrichments/{enrichment_id}` |
| `enrich:write` | `POST /merchant/v1/enrichments` and `POST /merchant/v1/enrichments/{enrichment_id}/refresh` |

A valid key without the required scope receives `403 insufficient_scope`, with the header `WWW-Authenticate: Bearer realm="noryo-merchant-api", error="insufficient_scope"`. This refusal happens after the rate-limit check: it consumes a token.

## Other authorisation refusals

| Code | Status | When |
|---|---|---|
| [`account_inactive`](https://developer.noryo.app/en-GB/errors/account_inactive) | 403 | The key's organisation or workspace is not active |
| [`production_not_enabled`](https://developer.noryo.app/en-GB/errors/production_not_enabled) | 403 | The key is not allowed to call Production |
| [`insufficient_scope`](https://developer.noryo.app/en-GB/errors/insufficient_scope) | 403 | The key lacks the route's scope |

## In this section

- [API keys](https://developer.noryo.app/en-GB/merchant/authentication/api-keys): anatomy, creation and the key list.
- [Environments](https://developer.noryo.app/en-GB/merchant/authentication/environments): Sandbox and Production, what is isolated.
- [Keeping secrets safe](https://developer.noryo.app/en-GB/merchant/authentication/secret-security): where the secret lives, what Noryo keeps of it.
- [Rotation](https://developer.noryo.app/en-GB/merchant/authentication/rotation): replacing a key without downtime.
- [Revocation](https://developer.noryo.app/en-GB/merchant/authentication/revocation): cutting a key off, and what survives.

---

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