# Authentication

URL: https://docs.router.ezat.io/authentication

> Send your ezat_sk_ key in x-api-key or Authorization Bearer, keep it safe, and fix 401 and 403 errors.

## When to use this page

Use this page to see how to send your API key, what a key looks like, how to keep keys safe, and what a `401` or `403` means.

## Send the key in a header

Every endpoint of both APIs takes your key in either of two headers:

| Header | Example | Sent by |
| --- | --- | --- |
| `x-api-key` | `x-api-key: ezat_sk_…` | The Anthropic SDKs, and Claude Code with `ANTHROPIC_API_KEY` |
| `Authorization` | `Authorization: Bearer ezat_sk_…` | The OpenAI SDKs, and Claude Code with `ANTHROPIC_AUTH_TOKEN` |

Both work on `https://api.router.ezat.io/v1/…` (Anthropic format) and on `https://api.router.ezat.io/openai/v1/…` (OpenAI format), so use whichever your tool sends.

```bash tab="x-api-key" test
curl --fail-with-body -sS https://api.router.ezat.io/v1/models \
  -H "x-api-key: $EZAT_API_KEY"
```

```bash tab="Authorization: Bearer" test
curl --fail-with-body -sS https://api.router.ezat.io/v1/models \
  -H "Authorization: Bearer $EZAT_API_KEY"
```

```bash tab="OpenAI format" test
curl --fail-with-body -sS https://api.router.ezat.io/openai/v1/models \
  -H "Authorization: Bearer $EZAT_API_KEY"
```

Details:

- If a request has both headers, `x-api-key` is used and `Authorization` is ignored. A stale key left in one variable can therefore hide the right key in the other (see [Troubleshooting](https://docs.router.ezat.io/troubleshooting.md)).
- Spaces before or after the key are ignored. `Bearer` can be in any case.
- Don't put the key in a URL or query string. It is only read from these headers.

## What a key looks like

A key is `ezat_sk_` followed by 40 letters and digits, 48 characters in all. The dashboard shows only its start and end, like `ezat_sk_a1b2…wxyz`, so you can tell keys apart.

EZAT Router stores only a hash of each key, so the full key is shown once, when you create it. A lost key can't be shown again: create a new one ([API keys](https://docs.router.ezat.io/api-keys.md)).

## Keys belong to an organization

Each key belongs to one organization. Requests made with it are paid from that organization's balance and show in that organization's usage, whoever made them. A key keeps working when the person who created it leaves the organization, until someone revokes it. See [Organizations and members](https://docs.router.ezat.io/organizations.md).

Signing in to the dashboard (email code, GitHub or Google) is separate: API keys work only on the API, and your dashboard sign-in never works as an API key.

## Keep your keys safe

- **Read the key from an environment variable** or your secret manager, never from a string in your code. These docs use `EZAT_API_KEY`.
- **Keep it on the server.** Never put a key in frontend, browser or mobile app code: anyone who opens the app can read it and spend your balance. Call EZAT Router from your backend instead.
- **Keep it out of git.** Put local secrets in a `.env` file that `.gitignore` excludes, and use your CI's secret store. Don't put it in a committed tool config such as a project's `.claude/settings.json`.
- **Don't share it in chat, tickets, logs or screenshots.** Support never needs your key; send the `request-id` instead.
- **Use one key per app, machine or person**, so you can revoke one without breaking the others, and see each one's spend under **Usage**. A [spend limit](https://docs.router.ezat.io/api-keys.md) caps what a leaked key can cost.
- **Rotate by replacing.** Create a new key, deploy it, check that requests succeed, then revoke the old key. Revoking takes effect immediately.
- **If a key leaks, revoke it at once** on the **API keys** page, then create a new one.

## Authentication errors

Errors use each API's own format. In the Anthropic format:

```json title="401 response"
{
  "type": "error",
  "error": { "type": "authentication_error", "message": "Invalid API key" },
  "request_id": "…"
}
```

| Status | Message | What it means | Fix |
| --- | --- | --- | --- |
| `401` | `Missing API key. Send it in the x-api-key or Authorization: Bearer header.` | No key arrived: the header is missing or misspelled, or the environment variable is empty in the process that sent the request | Set the header; check the variable is set where the tool runs |
| `401` | `Invalid API key` | The value isn't a working EZAT Router key: a typo, a cut-off copy, quotes or a line break inside it, or a key from another provider | Copy the key again or create a new one |
| `401` | `This API key has been revoked` | Someone revoked the key | Create a new key |
| `401` | `This API key has expired` | The key passed the expiry date set when it was created | Create a new key |
| `403` | `This organization is suspended. Contact support@ezat.io.` | Every key of the organization is refused | Email support@ezat.io |
| `403` | `This API key is not allowed to use …` | The key is limited to other models | Use an allowed model, or a key without a model list ([API keys](https://docs.router.ezat.io/api-keys.md)) |

In the OpenAI format the same errors have the same status. A `401` has `"type": "invalid_request_error"` and `"code": "invalid_api_key"`, and a `403` has `"type": "permission_error"`. Retrying doesn't help with any of them. [Errors](https://docs.router.ezat.io/reference/errors.md) lists every other error.

## For AI agents

1. Read the key from the environment variable the user names (`EZAT_API_KEY` by convention). Never ask the user to paste the key into the chat, and never print, log or commit it.
2. To check a key without printing it, compare its length (48) and its first 8 characters (`ezat_sk_`), then call `GET https://api.router.ezat.io/v1/models` and look for `200`.
3. Send it as `x-api-key` or `Authorization: Bearer`; both work on `https://api.router.ezat.io/v1/…` and `https://api.router.ezat.io/openai/v1/…`. If you set both, they must hold the same key, because `x-api-key` wins.
4. On `401` or `403`, stop and tell the user the message. Don't retry, and don't try other keys you find on the machine.
5. Never put the key in client-side code, a URL, or a file that is committed.