---
title: AI Gateway
description: Call OpenAI and Anthropic models through one OpenAI-compatible endpoint, stream replies, and see which model answered.
section: AI
order: 1
---

The AI Gateway is one OpenAI-compatible endpoint at `https://api.dekka.cloud/v1/ai` for every model your projects may use. You keep the OpenAI SDK you already have, point its base URL at the gateway and use a gateway key, and switching provider is a change to the model name. Each request is logged with its model, tokens, timing and cost, and you can limit what each project may use with [routing rules](/docs/ai-routing).

## Send your first request

Create a gateway key under **AI Gateway**, then **Keys**, as [AI keys](/docs/ai-keys) describes, and save it as `DEKKA_AI_KEY`. Then send a chat completion with a model from the [catalogue](/docs/ai-models), named by provider and model.

```ts tab="TypeScript"
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.dekka.cloud/v1/ai",
  apiKey: process.env.DEKKA_AI_KEY,
});

const completion = await client.chat.completions.create({
  model: "anthropic/claude-sonnet-5-5",
  messages: [{ role: "user", content: "Summarise this complaint in one sentence." }],
});

console.log(completion.choices[0].message.content);
```

```python tab="Python"
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.dekka.cloud/v1/ai",
    api_key=os.environ["DEKKA_AI_KEY"],
)

completion = client.chat.completions.create(
    model="anthropic/claude-sonnet-5-5",
    messages=[{"role": "user", "content": "Summarise this complaint in one sentence."}],
)

print(completion.choices[0].message.content)
```

```bash tab="cURL"
curl https://api.dekka.cloud/v1/ai/chat/completions \
  -H "Authorization: Bearer $DEKKA_AI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-5-5",
    "messages": [{"role": "user", "content": "Summarise this complaint in one sentence."}]
  }'
```

The reply is a `chat.completion` object in OpenAI's shape, with the model that answered as `model` and the token counts under `usage`. `GET /v1/ai/models` lists the models your key may use, in OpenAI's model list format.

## Stream the reply

Set `stream: true` and the gateway answers with server-sent events of `chat.completion.chunk`, ending in `data: [DONE]`. The last chunk before `[DONE]` has no choices and carries the usage, whether or not you set `stream_options.include_usage`, so read each chunk's choices with care.

```ts title="stream.ts"
const stream = await client.chat.completions.create({
  model: "anthropic/claude-sonnet-5-5",
  messages: [{ role: "user", content: "Write a short welcome message for new customers." }],
  stream: true,
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```

If the provider fails after the reply has started, the response is already a 200, so the stream carries an event with `error.code` and `error.message` and then `[DONE]`.

## See which provider and model answered

Every successful response carries two headers. They matter after a [fallback](/docs/ai-routing#how-does-fallback-work), when the model that answered isn't the one you asked for.

| Header | Value |
| --- | --- |
| `X-Dekka-Provider` | `openai`, `anthropic`, or `private` for a [private inference endpoint](/docs/gpu-instances#call-an-endpoint-through-the-ai-gateway) |
| `X-Dekka-Model` | The catalogue ID of the model that answered, such as `anthropic/claude-sonnet-5-5` |

With the OpenAI SDK for TypeScript, `withResponse()` gives you the headers alongside the reply.

```ts
const { data, response } = await client.chat.completions
  .create({ model: "anthropic/claude-sonnet-5-5", messages: [{ role: "user", content: "Hello" }] })
  .withResponse();

console.log(response.headers.get("X-Dekka-Model"), data.choices[0].message.content);
```

## What does the gateway accept?

The gateway takes OpenAI's chat completions request and ignores fields it doesn't know. Messages can have the roles `system`, `developer`, `user` and `assistant`, and their content must be text, so an image or any other part answers 422 `unsupported_content`. Tool calling isn't supported yet, and a `tools` field is ignored.

It passes on `max_tokens` or `max_completion_tokens`, `temperature`, `top_p`, up to four `stop` sequences and `user`. Anthropic needs a maximum, so the gateway sets `max_tokens` to 1024 when you send none. Claude models from 4.7 on accept only their default sampling settings, so `temperature` and `top_p` reach only Claude Haiku 4.5 and Claude Sonnet 4.5 among the Anthropic models.

## Where are prompts processed?

OpenAI and Anthropic process every prompt and reply on their own infrastructure in the United States, which is outside Ghana. Dekka keeps each request's model, token counts, timing and cost, and never stores the prompt or the reply.

> [!NOTE]
> Private inference endpoints, for prompts that can't leave the country, use the same endpoint and the same key. They're on the waitlist until GPU capacity exists, but you can request one now and build against it with a simulated reply, as [GPU instances](/docs/gpu-instances#request-a-private-inference-endpoint) describes.
