# OpenAI compatibility

> What the gateway supports, what it rejects and what it silently ignores, before you migrate.

Source: https://cairva.xyz/docs/compatibility

Cairva implements OpenAI-style text chat. Everything outside that is either rejected with an error or silently ignored, and it's better to know which before you migrate.

## Supported

- `POST /v1/chat/completions` with `system`, `user` and `assistant` messages
- Message content as a string or an array of text parts
- Streaming over server-sent events, with usage in the final chunk
- `max_tokens`, `max_completion_tokens`, `temperature`, `top_p`, `stop`, `user`
- `GET /v1/models`, scoped to your key
- The OpenAI error envelope, and `Retry-After` on `429`

## Rejected with 400

| Input | Result |
| --- | --- |
| Image, audio or file content parts | `validation_failed` |
| `tool`, `function` or `developer` roles | `validation_failed` |
| Assistant messages with `null` content, as produced by tool calls | `validation_failed` |
| More than 200 messages, or none | `validation_failed` |
| `max_tokens` above 32,768 | `validation_failed` |
| More than 4 stop sequences | `validation_failed` |

## Ignored

These fields are accepted and dropped. Your request runs as if you hadn't sent them.

| Field | What you get instead |
| --- | --- |
| `tools`, `tool_choice`, `functions` | No tool calls are ever returned |
| `n` | Always one choice |
| `response_format` | Plain text. Ask for JSON in the prompt and validate it yourself |
| `logprobs`, `top_logprobs` | No log probabilities |
| `seed`, `presence_penalty`, `frequency_penalty`, `logit_bias` | Not passed to the provider |
| `stream_options` | Usage is always sent in the last streamed chunk |
| Extra fields on messages, such as `name` | Dropped |

## Not available

Embeddings, images, audio, files, assistants, batches, the Responses API and the legacy completions endpoint. Only the four endpoints in the [introduction](https://cairva.xyz/docs#the-api-in-one-minute) exist.

## Differences in responses

- Every response has a `cairva` object. SDKs keep it as an extra field. See [SDKs](https://cairva.xyz/docs/sdks).
- `usage` can be `null` when the provider didn't report counts. Code that reads `usage.total_tokens` without a check will throw.
- Streamed deltas carry `content` only, with no initial `role` chunk.
- `model` in the response is Cairva's model id, which is what you sent.
- Reasoning isn't returned. See [Output tokens you don't see](https://cairva.xyz/docs/chat-completions#output-tokens-you-don-t-see).

## Migration checklist

1. Change the base URL to `https://cairva.xyz/v1` and the key to your Cairva key.
2. Set `model` to the id from `/v1/models`.
3. Remove tool calling and structured output, or handle them in your own code.
4. Guard every read of `usage` for `null`.
5. Set `max_tokens` deliberately, since it's held from your allowance.
6. Handle `402` and `403` allocation errors. They mean your agreement, not your code, needs attention.
