# Cloudflare AI Gateway

Source: https://staging-docs.aiand.com/integrations/cloudflare-ai-gateway/

[Cloudflare AI Gateway](https://developers.cloudflare.com/ai-gateway/) can proxy `https://api.aiand.com` as a [custom provider](https://developers.cloudflare.com/ai-gateway/configuration/custom-providers/). You get Gateway logs, analytics, cache, and rate limits; ai& still bills the `sk-` key.

ai& is not a native Gateway provider. The URL uses `custom-{slug}`, not `/openai` or `/aiand`.

## Setup

Create an [API key](https://console.aiand.com) first.

<Steps>

1. In the Cloudflare dashboard, open **AI → AI Gateway → Custom Providers** and add a provider:

   - Slug: `aiand`
   - Base URL: `https://api.aiand.com` (no trailing slash, no `/v1`)
   - Enable it

2. Create a gateway (any name; examples below use `aiand-gateway`).

3. Send the ai& key as `Authorization: Bearer sk-…`. **Authenticated Gateway** is optional on this path. If it is on, also send `cf-aig-authorization` with a Cloudflare token that has **AI Gateway Run**.

4. Or store the key under **Provider Keys** (BYOK) and omit `Authorization`. BYOK needs Authenticated Gateway on, and every request must send `cf-aig-authorization`. That Cloudflare token is not the `sk-` key.

</Steps>

The custom-provider form has no key field. That is expected.

## Call through the gateway

Replace `ACCOUNT_ID` with your Cloudflare account id.

```bash
curl https://gateway.ai.cloudflare.com/v1/ACCOUNT_ID/aiand-gateway/custom-aiand/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AIAND_API_KEY" \
  -H "cf-aig-authorization: Bearer $CF_AIG_TOKEN" \
  -d '{
    "model": "openai/gpt-oss-120b",
    "messages": [{"role": "user", "content": "Hello"}]
  }'
```

The examples include both headers. Drop `cf-aig-authorization` only when you send the `sk-` yourself and Authenticated Gateway is off. Drop `Authorization` only for BYOK, and keep `cf-aig-authorization` in that case.

```ts

const client = new OpenAI({
  apiKey: process.env.AIAND_API_KEY,
  baseURL: "https://gateway.ai.cloudflare.com/v1/ACCOUNT_ID/aiand-gateway/custom-aiand/v1",
  defaultHeaders: {
    "cf-aig-authorization": `Bearer ${process.env.CF_AIG_TOKEN}`,
  },
});

const response = await client.chat.completions.create({
  model: "openai/gpt-oss-120b",
  messages: [{ role: "user", content: "Hello" }],
});
```

The SDK appends `/chat/completions`. The gateway then calls `https://api.aiand.com/v1/chat/completions`. The same `baseURL` works for `/v1/responses`, `/v1/messages`, `/v1/models`, and `/v1/completions`.

<Aside type="caution">
  Do not put `/v1` on the custom provider base URL. A request to `…/custom-aiand/v1/chat/completions` would become `https://api.aiand.com/v1/v1/chat/completions`.
</Aside>

## Unified `/compat` (optional)

```bash
curl https://gateway.ai.cloudflare.com/v1/ACCOUNT_ID/aiand-gateway/compat/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AIAND_API_KEY" \
  -H "cf-aig-authorization: Bearer $CF_AIG_TOKEN" \
  -d '{
    "model": "custom-aiand/openai/gpt-oss-120b",
    "messages": [{"role": "user", "content": "Hello"}]
  }'
```

`/compat` is chat completions only. Prefer the provider-specific `/custom-aiand/v1` path for Responses, Messages, and the catalog.

## Caching

Turn on **Cache Responses** in the gateway settings. Identical requests return `cf-aig-cache-status: HIT` and do not reach ai& (and are not billed). A cache miss is `MISS`.

The default cache key is an exact match of provider, path, model, auth, and the full JSON body. Bypass with `cf-aig-skip-cache: true`.

<Aside type="caution">
  `cf-aig-cache-key` replaces that exact-match key. Two different prompts that share one custom key get the same cached answer. Do not use a shared custom key for user-specific chat.
</Aside>

Streaming is not cached by default. **Cache Responses** covers identical non-streaming text and image requests only. A `stream: true` call still reaches ai& and is billed.

## Errors

- Missing or bad `cf-aig-authorization` (when auth is on): Cloudflare `401` (`AiGatewayError`), not an OpenAI error body.
- Wrong custom slug: Cloudflare `502` (`The provider did not return a valid response`), not ai&'s `404`.
- Unknown model, bad JSON, empty `messages`, invalid `reasoning_effort`, vision on a text-only model: ai&'s usual OpenAI-shaped errors, passed through.

## What this is not

Calling `env.AI.run("@cf/…")` is [Workers AI](https://developers.cloudflare.com/workers-ai/), a different product. A Worker can call ai& with the OpenAI SDK and `baseURL: "https://api.aiand.com/v1"` without AI Gateway at all.
