# Gateway reference

Look up the gateway addresses, authentication, headers, endpoint behaviour and limits. For how the gateway works and why, see [Gateway concepts](https://docs.atlas.ilionx.dev/concepts/gateway.md). For error formats, see [Errors](https://docs.atlas.ilionx.dev/reference/errors.md).

**At a glance**

- Call OpenAI-style endpoints under `https://<atlas-host>/gateway/openai/v1`, and Anthropic Messages under `https://<atlas-host>/gateway/anthropic`.
- Authenticate with an ATLAS API key. The key alone decides the space or project the request runs under. The gateway ignores any space or project header you send.
- Name the model as `provider-slug/model-slug`, for example `openai/gpt-4.1`.
- Every request is checked against your model and budget rules before it reaches a provider.

## Endpoints

All paths are relative to `https://<atlas-host>/gateway`.

| Method | Path                                   | Request                                                              | Response                          | Purpose                                                  |
| ------ | -------------------------------------- | -------------------------------------------------------------------- | --------------------------------- | -------------------------------------------------------- |
| `POST` | `/openai/v1/chat/completions`          | JSON                                                                 | JSON or SSE                       | Chat completions on any provider                         |
| `POST` | `/openai/v1/responses`                 | JSON                                                                 | JSON or SSE                       | OpenAI Responses API                                     |
| `POST` | `/openai/v1/embeddings`                | JSON                                                                 | JSON                              | Text embeddings                                          |
| `POST` | `/openai/v1/rerank`                    | JSON                                                                 | JSON                              | Document reranking (Cohere v2 / Jina shape)              |
| `POST` | `/openai/v1/audio/transcriptions`      | `multipart/form-data`                                                | Provider's content type           | Speech to text                                           |
| `POST` | `/openai/v1/audio/speech`              | JSON                                                                 | Audio bytes                       | Text to speech                                           |
| `GET`  | `/openai/v1/models`                    | Optional `?endpoint=chat\|embeddings\|rerank\|transcription\|speech` | `{object:"list", data:[…]}`       | Models your key can use. An unknown filter returns `400` |
| `GET`  | `/openai/v1/models/{model}`            | `{model}` is `provider-slug/model-slug`                              | Model entry                       | One model                                                |
| `GET`  | `/openai/v1/models/{model}/parameters` | None                                                                 | `{model, supported_parameters[]}` | Parameters the gateway passes on for the model           |
| `POST` | `/anthropic/v1/messages`               | JSON                                                                 | JSON or Anthropic SSE             | Anthropic Messages API (Claude models only)              |
| `POST` | `/anthropic/v1/messages/count_tokens`  | JSON                                                                 | JSON                              | Token count (no budget check, no cost)                   |
| `GET`  | `/anthropic/v1/models`                 | `limit`, `before_id`, `after_id`                                     | Anthropic model list              | Claude models your key can use                           |
| `GET`  | `/anthropic/v1/models/{model}`         | `{model}` may be `%2F`-encoded                                       | Anthropic model entry             | One Claude model                                         |
| `POST` | `/openai/v1/completions`               | None                                                                 | None                              | Not offered. Use `/openai/v1/chat/completions`.          |

### Model entry

Each entry in `/openai/v1/models` has these fields:

- `id`, `object`, `created`, `owned_by`
- `provider_scope`, `provider_type`, `model_name`, `status`
- `max_input_tokens`, `max_tokens`, `context_length`
- `endpoints[]`, `top_provider.max_completion_tokens`
- A `capabilities` object with `temperature_supported`, `reasoning`, `reasoning_options`, `tool_call`, `structured_output`, `context_limit`, `output_limit`, `status` and `modalities`

### Anthropic model entry

Each entry has `type`, `id`, `display_name` and `created_at`. `created_at` is RFC 3339 in UTC and holds the date the model was added to ATLAS.

| Rule                                | Value                        |
| ----------------------------------- | ---------------------------- |
| `limit`                             | 1 to 1000, default 20        |
| `before_id` and `after_id` together | `400`                        |
| Cursor your key cannot see          | `400`                        |
| Order                               | Newest first, then by `id`   |
| Hidden or unknown model             | `404` with `not_found_error` |

## Authentication

| Rule                | Behaviour                                                                                                                                                                      |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Header              | `Authorization: Bearer <your-atlas-key>`                                                                                                                                       |
| Anthropic addresses | Also accept `x-api-key: <your-atlas-key>`. Sending both with the same key is fine. Different keys return `401`.                                                                |
| OpenAI addresses    | Bearer only                                                                                                                                                                    |
| `401`               | The key is missing, invalid, expired, revoked or conflicting. The answer is identical for every cause, so keys cannot be probed. Requests with an invalid key aren't recorded. |
| Key types           | Personal keys (act under a space or a project) and project keys. See [API keys](https://docs.atlas.ilionx.dev/reference/api-keys.md).                                                                                  |

A valid key can still be refused. The error tells you why: `403` `organization_suspended`, `project_archived` or `no_acting_context`. See [Errors](https://docs.atlas.ilionx.dev/reference/errors.md).

## Headers

| Header                                              | Direction | Notes                                                                                                      |
| --------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------- |
| `Authorization`                                     | Request   | Bearer ATLAS key                                                                                           |
| `x-api-key`                                         | Request   | Anthropic addresses only                                                                                   |
| `anthropic-version`                                 | Request   | Passed on. Your value wins.                                                                                |
| `anthropic-beta`                                    | Request   | Passed on to Claude models. Flags a provider does not support may be removed so your client keeps working. |
| `Accept`                                            | Request   | Ignored on streaming endpoints. The `stream` flag in the body decides.                                     |
| `traceparent`, `tracestate`, `atlas-correlation-id` | Request   | Optional. The correlation id is kept in the audit log, so you can match a request to your own logs.        |
| `atlas-request-id`                                  | Response  | The ATLAS request id. Also present in error bodies. Quote it to your ilionx contact.                       |
| `request-id`                                        | Response  | Anthropic addresses only. Same value as `atlas-request-id`.                                                |
| `Retry-After`                                       | Response  | On `provider_rate_limited` and availability errors.                                                        |

Your credentials are never forwarded to the provider.

## Chat completions

`POST /openai/v1/chat/completions` is OpenAI-compatible. `model` is a reference of the form `provider-slug/model-slug`. A bare model name returns `404 model_not_found`.

- ATLAS understands `model`, `messages`, `temperature`, `max_tokens` and `stream`. Other parameters are passed to the provider where the model supports them. See [Unsupported parameters](https://docs.atlas.ilionx.dev/reference/gateway.md#unsupported-parameters).
- `max_completion_tokens` is accepted. If you send both `max_tokens` and `max_completion_tokens`, `max_completion_tokens` wins and only one limit reaches the provider.
- `tools` and `tool_choice` work on OpenAI, Azure OpenAI, Anthropic and Ollama models. See [Tool calling](https://docs.atlas.ilionx.dev/reference/gateway.md#tool-calling).
- Dropped parameters are listed in a top-level `atlas_stripped_parameters` array, or on the first chunk only when streaming. The array is present only when something was dropped.
- Claude thinking blocks appear as `message.reasoning_content` (non-streaming) or `delta.reasoning_content` (streaming).
- `content` is always present and `null` when the turn produced no visible text.
- A streamed response never contains an empty `"content":""` chunk.

### Streaming

| Rule                                 | Value                                                                                             |
| ------------------------------------ | ------------------------------------------------------------------------------------------------- |
| Selector                             | The top-level `stream` flag in the body. Nested `stream` keys are ignored.                        |
| Applies to                           | Chat completions, Responses and Anthropic Messages                                                |
| Format                               | `data: {…}` frames ending with `data: [DONE]` (OpenAI). Anthropic Messages returns Anthropic SSE. |
| Embeddings and rerank                | Not streamed. `stream` has no effect.                                                             |
| Transcription                        | `stream=true` returns `400`                                                                       |
| Speech                               | `stream_format=sse` is refused                                                                    |
| Failure before the first chunk       | A normal HTTP error                                                                               |
| Failure after the first chunk        | One final error frame in the format of the address you called, then the stream ends               |
| Provider closes the connection early | `provider_stream_interrupted` (`502`)                                                             |

Tokens used before a failure or a client cancel are still counted and billed.

#### Trailing usage chunk

Before `[DONE]`, a final chunk with `choices: []` carries token usage:

```json
{
  "id": "…",
  "object": "chat.completion.chunk",
  "model": "…",
  "choices": [],
  "usage": {
    "prompt_tokens": 123,
    "completion_tokens": 45,
    "total_tokens": 168,
    "prompt_tokens_details": { "cached_tokens": 80 }
  }
}
```

| Rule                                 | Behaviour                                                                                                                                 |
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| OpenAI and Azure OpenAI models       | The provider's own usage chunk passes through unchanged                                                                                   |
| Anthropic, Ollama and Bedrock models | The gateway creates the chunk. `stream_options.include_usage` set to `true` or `false` in your request wins. Otherwise the chunk is sent. |
| Never invented                       | Omitted when no tokens were observed, and after a failure mid-stream                                                                      |
| Claude cache tokens                  | Cache read and cache creation tokens are included in `prompt_tokens`. Cache reads also show as `prompt_tokens_details.cached_tokens`.     |

### Tool calling

| Provider             | Behaviour                                                                                                                                                                                                                                                                                                    |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| OpenAI, Azure OpenAI | Native and unchanged                                                                                                                                                                                                                                                                                         |
| Anthropic            | `tool_choice` values `"auto"`, `"none"`, `"required"` or a named function are translated. `parallel_tool_calls: false` disables parallel tool use. Unrecognisable forms fail with `400` naming `tool_choice`. Tool results and replayed tool calls are converted both ways. `finish_reason` is `tool_calls`. |
| Ollama               | `tools` are forwarded. `tool_choice` `"auto"` is dropped. `"none"` sends no tools. `"required"` or a named function returns `400`. Responses are normalised to the OpenAI shape for streaming and non-streaming alike.                                                                                       |

## Responses API

`POST /openai/v1/responses` accepts the OpenAI Responses format for any chat model. The model decides whether ATLAS passes the request straight to the provider or translates it:

- A model that supports the Responses API natively receives the request unchanged, apart from the model name.
- A chat-only model (Anthropic, Ollama, AWS Bedrock, or any model configured for chat) is served by translating the request to chat and the answer back.
- A chat request to a model configured for Responses is translated the other way.

| Rule                                     | Behaviour                                                                     |
| ---------------------------------------- | ----------------------------------------------------------------------------- |
| Rejected when translated to chat (`400`) | `previous_response_id`, `store: true`, `conversation` and built-in tools      |
| Fields that cannot be mapped             | Dropped and reported in `atlas_stripped_parameters`                           |
| `atlas_stripped_parameters` placement    | Top level when not streaming. On `response.created` when streaming.           |
| Streaming terminal event                 | Exactly one of `response.completed`, `response.failed`, `response.incomplete` |
| Provider failure on a translated request | `response.failed`                                                             |
| Usage                                    | `input`, `output`, cached-read and reasoning tokens                           |

## Embeddings

`POST /openai/v1/embeddings` takes `{model, input: string | string[], encoding_format?, dimensions?, user?}`.

| Rule        | Behaviour                                                                                                              |
| ----------- | ---------------------------------------------------------------------------------------------------------------------- |
| Eligibility | The model must offer embeddings. Otherwise `400 model_not_supported_for_endpoint` and nothing is sent to the provider. |
| Batching    | One request is one cost and one budget decision, however many inputs it holds                                          |
| Cost        | Input tokens only                                                                                                      |
| Storage     | Vectors are never stored                                                                                               |

- The gateway accepts a list of any length for every embedding model. If the model limits inputs per upstream call, the gateway splits the list into several calls and runs them in parallel. Cohere Embed models on AWS Bedrock take 96 inputs per call. Amazon Titan Text Embeddings v2 takes one input per call, so a list of 50 inputs makes 50 calls. Other models take the whole list in one call.
- The gateway merges the results in input order. It records one audit entry and one cost for the request, based on the summed input tokens of all calls.
- If any call fails, the whole request fails. Tokens from calls that completed are still billed.
- `input_type` applies to Cohere Embed models on AWS Bedrock only. Values are `search_document` (default), `search_query`, `classification` or `clustering`. Any other value returns `400`. Other models ignore it.
- With `encoding_format: base64`, vectors are returned as little-endian 32-bit floats.

## Rerank

`POST /openai/v1/rerank` needs a rerank model.

| Field              | Rule                                                 |
| ------------------ | ---------------------------------------------------- |
| `query`            | Required                                             |
| `documents`        | 1 to 1000 strings. Structured documents return `400` |
| `top_n`            | At least 1. Clamped to the number of documents sent. |
| `return_documents` | Optional                                             |

Results are sorted by descending score and capped to `top_n`. The gateway fills in the document text from your request and always returns `usage`.

## Transcription

`POST /openai/v1/audio/transcriptions` accepts `multipart/form-data` and needs a transcription model.

| Field                       | Rule                                         |
| --------------------------- | -------------------------------------------- |
| `file`                      | Required, exactly one                        |
| `model`                     | Required                                     |
| `language`, `prompt`        | Optional                                     |
| `response_format`           | `json`, `text`, `srt`, `verbose_json`, `vtt` |
| `temperature`               | 0 to 2                                       |
| `timestamp_granularities[]` | Optional                                     |

A missing `file` or `model`, a duplicate `file`, an invalid `temperature` or `stream=true` returns `400`. The provider's answer comes back unchanged with its content type. Audio is never stored.

## Speech

`POST /openai/v1/audio/speech` needs a speech model.

| Field                   | Rule                                       |
| ----------------------- | ------------------------------------------ |
| `model`                 | Required                                   |
| `input`                 | Up to 4096 characters                      |
| `voice`                 | Required by OpenAI-compatible providers    |
| `response_format`       | `mp3`, `opus`, `aac`, `flac`, `wav`, `pcm` |
| `speed`, `instructions` | Optional                                   |
| `stream_format=sse`     | Refused                                    |

Audio streams back with the provider's content type. Audio is never stored.

## Anthropic Messages

Claude Code, Anthropic SDKs and opencode use the Anthropic addresses to reach Claude through ATLAS. For client setup, see [Connect an OpenAI- or Anthropic-compatible client](https://docs.atlas.ilionx.dev/guides/user/gateway-connect-openai-and-anthropic-clients.md).

| Setting     | Value                                                                             |
| ----------- | --------------------------------------------------------------------------------- |
| Base URL    | `https://<atlas-host>/gateway/anthropic`                                          |
| Credentials | Your ATLAS API key, sent as `x-api-key` or as a Bearer token                      |
| `model`     | The ATLAS model reference (`provider-slug/model-slug`), not the provider's own id |

The request body is forwarded as you sent it. Only the model id is replaced.

| Rule           | Behaviour                                                                                                                                  |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| Rejections     | Unknown model: not found. Non-Claude model: `400`. Server-side tools: `400` (allowed on `count_tokens`). Malformed body: `400`.            |
| `count_tokens` | Same authentication and model access as Messages, but no budget check and no cost. ATLAS never estimates counts. The provider counts them. |

## Model resolution

The key decides where a model name is looked up.

| Credential                 | Models available                                                                               |
| -------------------------- | ---------------------------------------------------------------------------------------------- |
| Personal key               | The organisation's models first, then models provided by ilionx, minus any the space restricts |
| Key acting under a project | Only the project's own model list                                                              |

| Condition                                                                                     | Error                                  |
| --------------------------------------------------------------------------------------------- | -------------------------------------- |
| Bare name, empty part, more than one `/`, unknown, inactive, or hidden by a space restriction | `404 model_not_found`                  |
| The model's provider is disabled                                                              | `403 provider_disabled`                |
| The project has no models                                                                     | `404 project_has_no_models`            |
| The model cannot serve the endpoint you called                                                | `400 model_not_supported_for_endpoint` |

A model that the space restricts or hides returns `404 model_not_found`, the same as a model that does not exist.

## Unsupported parameters

ATLAS removes some parameters a model does not support so the request still succeeds. Removed parameters are listed in `atlas_stripped_parameters` in the response.

ATLAS does not silently remove parameters that change how you must read the response: `n`, `logprobs`, `top_logprobs` and `response_format`. On providers that cannot honour them, the request fails with `400` naming the field.

ATLAS adds stored model defaults for any parameter you omitted (JSON `null` counts as omitted). Your values always win, including `none` and `0`. Admins can adjust parameter handling per provider. See [Tune how a provider handles request parameters](https://docs.atlas.ilionx.dev/guides/admin/gateway-tune-provider-parameter-handling.md).

### Reasoning parameters

ATLAS understands these reasoning parameters:

- `reasoning_effort` (OpenAI, Anthropic, AWS Bedrock)
- `reasoning_budget_tokens` and `reasoning_enabled` (Anthropic, AWS Bedrock)

Claude thinking blocks appear as `reasoning_content`. OpenAI-family models receive `reasoning_effort` as sent.

## Provider failures

- ATLAS retries temporary provider failures automatically before it returns an error.
- Streams that have already started are not retried.
- A provider may still bill a failed first attempt.
- When a provider keeps failing, you receive one of the errors listed in [Errors](https://docs.atlas.ilionx.dev/reference/errors.md#common-gateway-codes).
- Your key was valid even if the error mentions the provider's credentials. The problem is on the provider side.
- Quote the `atlas-request-id` to your ilionx contact.

## Budgets

ATLAS checks budgets before it sends the request. A used-up limit returns `429 budget_exceeded` (OpenAI addresses) or `402` with `billing_error` (Anthropic addresses). For the rules, see [Budgets](https://docs.atlas.ilionx.dev/reference/budgets.md).

## Limits

| Limit                       | Value                                                                                                        |
| --------------------------- | ------------------------------------------------------------------------------------------------------------ |
| Request body                | 25 MB. Larger returns `413 request_too_large`.                                                               |
| Rerank documents            | 1 to 1000 strings                                                                                            |
| Speech `input`              | 4096 characters                                                                                              |
| Transcription `temperature` | 0 to 2                                                                                                       |
| Anthropic models `limit`    | 1 to 1000 (default 20)                                                                                       |
| Provider connect timeout    | 10 seconds                                                                                                   |
| Provider response timeout   | 180 seconds by default. An Admin can set 1 to 600 seconds per provider.                                      |
| Provider retries            | Up to 2 retries on `429`, `500`, `502`, `503`, `504` and connection errors. Started streams are not retried. |

## Related

- [Errors](https://docs.atlas.ilionx.dev/reference/errors.md)
- [API keys](https://docs.atlas.ilionx.dev/reference/api-keys.md)
- [Budgets](https://docs.atlas.ilionx.dev/reference/budgets.md)
- [Glossary](https://docs.atlas.ilionx.dev/reference/glossary.md)
- [Reference index](https://docs.atlas.ilionx.dev/reference.md)
