# Notes for AI agents

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

> Required values, a safe setup procedure and a do-not list for AI agents that set up tools or code to use EZAT Router.

## When to use this page

Read this page if you are an AI agent (Claude Code, Cursor, Codex, ChatGPT or another) setting up a tool, an SDK or code to call AI models through EZAT Router for a user, or debugging such a setup.

## Required values

| What | Value |
| --- | --- |
| Anthropic-format base URL | `https://api.router.ezat.io` (clients add `/v1/messages` themselves: don't add `/v1`) |
| OpenAI-format base URL | `https://api.router.ezat.io/openai/v1` |
| Auth header | `x-api-key: $EZAT_API_KEY` or `Authorization: Bearer $EZAT_API_KEY`, on both APIs |
| Key format | `ezat_sk_` followed by 40 letters and digits (48 characters) |
| Key variable | `EZAT_API_KEY`, unless the user names another |
| Model discovery | `GET https://api.router.ezat.io/v1/models` (or `GET https://api.router.ezat.io/openai/v1/models`) with the key |
| Model ids | `author/model`, such as `anthropic/claude-sonnet-5-5`. Anthropic's own names are aliases of the same models. |
| Catalog with aliases, limits and prices (no key) | `GET https://api.router.ezat.io/api/models` |
| Messages endpoint | `POST https://api.router.ezat.io/v1/messages` |
| Chat Completions endpoint | `POST https://api.router.ezat.io/openai/v1/chat/completions` |
| Dashboard (keys, credit, usage) | `https://router.ezat.io`: keys at `https://router.ezat.io/keys`, credit at `https://router.ezat.io/billing` |
| Support | support@ezat.io, quoting the `request-id` response header |

Optional headers, for code you write:

| Header | Effect |
| --- | --- |
| `X-EZAT-Session: <any stable id>` | Keeps the requests of one conversation on the same capacity, so prompt caching keeps working. Without it, EZAT Router groups requests by their system prompt and first message, which is enough for most clients. On the OpenAI-compatible API, `prompt_cache_key` does the same. |
| `x-ezat-cache: off` | OpenAI-compatible API only: turns off automatic prompt caching for this request. |

See [Prompt caching](https://docs.router.ezat.io/guides/prompt-caching.md).

## Safe setup procedure

1. **Find the key without seeing it.** Ask the user which environment variable holds their EZAT Router key; suggest `EZAT_API_KEY`. Never ask them to paste the key into the chat. If they have no key, tell them to create one at `https://router.ezat.io/keys` and export it in their own terminal.
2. **Check it's set, without printing it.** Print only its length and prefix:

   ```bash
   printf 'length %s, prefix %s\n' "${#EZAT_API_KEY}" "${EZAT_API_KEY:0:8}"
   ```

   Expect `length 48, prefix ezat_sk_`. A different length usually means quotes, spaces or a cut-off copy.
3. **Validate the key** with a request that costs nothing:

   ```bash tab="curl" test
   curl --fail-with-body -sS -o /dev/null -w '%{http_code}\n' https://api.router.ezat.io/v1/models \
     -H "x-api-key: $EZAT_API_KEY"
   ```

   `200` means the key works. `401` means it is missing, mistyped, expired or revoked: stop and tell the user.
4. **Pick a model.** Use the model the user asks for, or one from `GET https://api.router.ezat.io/v1/models`. Don't rely on a list from memory or from this page.
5. **Send one tiny request** with a small `max_tokens`:

   ```bash tab="curl" test
   curl --fail-with-body -sS https://api.router.ezat.io/v1/messages \
     -H "x-api-key: $EZAT_API_KEY" \
     -H "anthropic-version: 2023-06-01" \
     -H "content-type: application/json" \
     -d '{"model": "anthropic/claude-sonnet-5-5", "max_tokens": 16, "messages": [{"role": "user", "content": "Reply with OK."}]}'
   ```

   On `402`, the organization has no credit or the key reached its spend limit: tell the user to add credit at `https://router.ezat.io/billing` (or use another key), and stop.
6. **Configure the tool.** Use the format the tool speaks:
   - Claude Code and Anthropic SDKs: base URL `https://api.router.ezat.io`. For Claude Code, `ANTHROPIC_BASE_URL` and `ANTHROPIC_AUTH_TOKEN` ([Claude Code](https://docs.router.ezat.io/tools/claude-code.md)).
   - OpenAI SDKs and OpenAI-format tools: base URL `https://api.router.ezat.io/openai/v1` ([OpenAI-format tools](https://docs.router.ezat.io/tools/openai-tools.md)).
   - Your own code: read the key from the environment variable at runtime, on the server.

   Put the key in user-level or untracked configuration only. Ask before changing a global config file the user didn't mention.
7. **Verify inside the tool.** Make one request through the tool itself. In Claude Code, `/status` shows the base URL and which credential is in use.
8. **On errors, follow the error type.** Retry `429`, `500` and `529` (`503` in the OpenAI format) with backoff, waiting at least the seconds in the `retry-after` header when there is one. A `504` on a non-streaming request means the model took too long: send it again with streaming. Don't retry `400`, `401`, `402`, `403`, `404` or `413`: fix the cause first. See [Errors](https://docs.router.ezat.io/reference/errors.md) and [Troubleshooting](https://docs.router.ezat.io/troubleshooting.md).

## Do not do this

- Don't ask the user to paste a key into the chat, and don't print, echo or log a key, even partly beyond its `ezat_sk_` prefix.
- Don't write a key into a committed file: source code, a tracked `.env`, a project's `.claude/settings.json`, a CI config in the repository.
- Don't put a key in frontend, browser or mobile app code, or in a URL or query string.
- Don't add `/v1` to `https://api.router.ezat.io`, and don't give `https://api.router.ezat.io` to an OpenAI client: it needs `https://api.router.ezat.io/openai/v1`.
- Don't hard-code prices or model lists. Read `GET https://api.router.ezat.io/v1/models`, [Models](https://docs.router.ezat.io/models.md) and [Pricing](https://docs.router.ezat.io/pricing.md) when you need them.
- Don't sign up, pay, top up, or create or revoke keys for the user. They do those in the dashboard.
- Don't overwrite an existing Anthropic or OpenAI setup that the user uses for other work without asking. Change only what the task needs.
- Don't retry a `402` in a loop, and don't send large or long requests just to test a setup.
- Don't rely on Message Batches, the Files API, server tools that run on Anthropic's side (web search, code execution) or the MCP connector: EZAT Router doesn't serve them.

## Machine-readable docs

| File | What |
| --- | --- |
| `https://docs.router.ezat.io/llms.txt` | Index: the key values, then every page with a one-line description |
| `https://docs.router.ezat.io/llms-full.txt` | Every page in one Markdown file, with the live model and price tables filled in |
| `https://docs.router.ezat.io/<page>.md` | One page as Markdown, for example `https://docs.router.ezat.io/quickstart.md`. The page URL also returns Markdown when you send `Accept: text/markdown`. |
| `https://docs.router.ezat.io/openapi.yaml` | The public API spec (OpenAPI 3) |
| `https://docs.router.ezat.io/skill/ezat-router.zip` | The `ezat-router` agent skill. See [Agent skill](https://docs.router.ezat.io/agents/skill.md). |

Read only the pages you need. Prices and the model list change; fetch them when you need them instead of keeping a copy.