Skip to content

AI Gateway

Set up AI Gateway, Authenticated Gateway and third-party models, and use session affinity and cached tokens.

Updated

Cloudflare AI Gateway is a proxy for AI requests. It adds request logs, cost analytics, caching and rate limits in the Cloudflare dashboard. You create and configure a gateway in the dashboard. The package only sends requests to it.

To compare the gateway route with the direct route, see Direct and gateway routes.

Route requests through a gateway

  1. Create a gateway

    In the Cloudflare dashboard, go to AI, then AI Gateway, then Create Gateway. Copy the gateway name. This name is the slug.

  2. Add the slug to your .env file

    Add this line to your application’s .env file:

    CLOUDFLARE_AI_GATEWAY=my-gateway
  3. Read it in config/ai.php

    Add the gateway line to the provider entry. It reads the value from .env, so do not write the slug here.

    'workers-ai' => [
        'driver'     => 'workers-ai',
        'key'        => env('CLOUDFLARE_AI_API_TOKEN'),
        'account_id' => env('CLOUDFLARE_ACCOUNT_ID'),
        'gateway'    => env('CLOUDFLARE_AI_GATEWAY'),
    ],
  4. Send a request and open the logs

    Your calling code does not change. Open the gateway in the dashboard and go to Logs. Text and embedding calls show under compat.

Text and embedding calls go to https://gateway.ai.cloudflare.com/v1/<account_id>/<gateway>/compat. Clef and Clairic calls go to api.cloudflare.com with a cf-aig-gateway-id header, and Cloudflare applies the gateway’s features to them.

Use only the slug. If you give a URL or a value with a slash, the package throws and tells you to use the url key.

Authenticated Gateway

With Authenticated Gateway on, gateway.ai.cloudflare.com rejects a request that has no gateway token. To set it up:

  1. Open the gateway settings in the dashboard.
  2. Create an authentication token. This is the gateway token, and it has Run permission. Copy it.
  3. Turn on Authenticated Gateway.
  4. Add the token to your application’s .env file:
CLOUDFLARE_AI_GATEWAY_TOKEN=your-gateway-token
  1. Read it in the provider entry in config/ai.php:
'workers-ai' => [
    // ...
    'gateway_token' => env('CLOUDFLARE_AI_GATEWAY_TOKEN'),
],

The package sends cf-aig-authorization: Bearer <gateway_token>. Your key still goes in Authorization. If gateway_token is empty, the package does not send the header.

Calls to api.cloudflare.com, such as Clef and Clairic, do not need the gateway token. That API reads your API token from Authorization. Tested on 2026-11-05. FLUX.2 image models do need it. See Non-JSON models.

Without the header, or with a wrong token, an authenticated gateway returns HTTP 401 with {"code":2009,"message":"Unauthorized"}. Laravel throws Illuminate\Http\Client\RequestException. The message does not name the gateway or the header, so check gateway_token first.

Direct route with a gateway header

Cloudflare’s REST API on api.cloudflare.com also routes through a gateway. Send the cf-aig-gateway-id header on the direct route. It needs no gateway key, and no second token even on an authenticated gateway. FLUX.2 is the exception, and nova-3 cannot use a gateway. See Non-JSON models.

Keep the slug in your application’s .env file, as CLOUDFLARE_AI_GATEWAY, and read it in config/ai.php:

'workers-ai' => [
    'driver'     => 'workers-ai',
    'key'        => env('CLOUDFLARE_AI_API_TOKEN'),
    'account_id' => env('CLOUDFLARE_ACCOUNT_ID'),
    'headers'    => ['cf-aig-gateway-id' => env('CLOUDFLARE_AI_GATEWAY')],
],

Text, Clef, Clairic and embeddings all returned HTTP 200 this way on 2026-11-05. Cloudflare’s gateway docs list chat completions on this API, but not embeddings. See the AI Gateway REST API.

Third-party models

Cloudflare’s REST API serves other providers’ models with your Cloudflare token. Use the direct route, and name the model in author/model form:

use function Laravel\Ai\agent;

agent()->prompt('Hello', provider: 'workers-ai', model: 'openai/gpt-5.5');
  • Cloudflare bills the call from AI Gateway credits, or uses a key stored under the default alias. See Unified Billing and BYOK.
  • The call goes through your account’s default gateway. To pick another, add the cf-aig-gateway-id header as in the previous section.
  • The token needs Account, Workers AI, Read, the same as other calls. Tested on 2026-11-05 with openai/gpt-4.1-mini.

Use another provider’s driver through a gateway

You can point another Laravel AI driver at a gateway to get logs and caching. Use your own provider key, because Laravel AI’s drivers always send key as Authorization. Add the key to your application’s .env file as OPENAI_API_KEY. BaseUrl::providerUrl() builds the URL in config/ai.php:

use Meirdick\WorkersAi\Cloudflare\BaseUrl;

'openai' => [
    'driver'  => 'openai',
    'key'     => env('OPENAI_API_KEY'),
    'url'     => BaseUrl::providerUrl(env('CLOUDFLARE_ACCOUNT_ID'), env('CLOUDFLARE_AI_GATEWAY'), 'openai'),
    // Remove this line unless the gateway is authenticated.
    'headers' => ['cf-aig-authorization' => 'Bearer '.env('CLOUDFLARE_AI_GATEWAY_TOKEN')],
],

Replace 'openai' with 'anthropic' or another provider slug. Do not call config() here, because config/ai.php is still loading. To use a stored key instead, use third-party models.

Use the provider path

To use <gateway>/workers-ai/v1 instead of /compat, set gateway_path:

'gateway_path' => 'workers-ai/v1',

/compat stays the default. See The gateway provider path.

Use a custom URL

Set url for a full URL, such as a proxy. Add it to your application’s .env file as CLOUDFLARE_AI_URL, and read it in config/ai.php. Do not set account_id with it, because the package throws if both are set.

'workers-ai' => [
    'driver' => 'workers-ai',
    'key'    => env('CLOUDFLARE_AI_API_TOKEN'),
    'url'    => env('CLOUDFLARE_AI_URL'),
],

If the URL ends in /compat, the package adds the workers-ai/ model prefix, and Clef and Clairic throw. For any other URL, it sends the bare model name. See Where each call goes.

Session affinity

Set session_affinity to send the x-session-affinity header on every request:

'workers-ai' => [
    // ...
    'session_affinity' => 'my-app-session',
],

Related requests then reach the same cache shard, which helps the model prefix cache. See the next section.

Cached tokens

Some models cache the start of a prompt, and a cache hit costs fewer neurons. Cloudflare reports the cached count in usage.prompt_tokens_details.cached_tokens.

The two laravel/ai lines count differently:

  • laravel/ai 0.9 to 0.11. Usage::$promptTokens is the uncached count. Usage::$cacheReadInputTokens is the cached count. The two add up to the prompt_tokens that Cloudflare reports.
  • laravel/ai 1.x. The package returns TextUsage. inputTokens is the prompt_tokens that Cloudflare reports, with cached tokens included. cacheReadInputTokens is the cached part of it. It is null when the response has no cache field. outputTokens is the completion count.

WorkersAiUsageReported::$promptTokens is the uncached count on both lines.

// laravel/ai 0.9 to 0.11
$uncached = $response->usage->promptTokens;
$cached   = $response->usage->cacheReadInputTokens;

// laravel/ai 1.x
$total    = $response->usage->inputTokens;
$cached   = $response->usage->cacheReadInputTokens ?? 0;
$uncached = $total - $cached;

Two identical calls on @cf/zai-org/glm-5.3-flash, under one x-session-affinity value, measured on 2026-09-16:

Call Wire prompt_tokens Wire cached_tokens 0.x promptTokens 1.x inputTokens cacheReadInputTokens Neurons
1 1221 0 1221 1221 0 17.8
2 1221 1216 5 1221 1216 8.3

The cache depends on the model. In tests, @cf/zai-org/glm-5.3-flash and @cf/moonshotai/kimi-k2.6 hit the cache. @cf/meta/llama-3.3-70b-instruct-fp8-fast, @cf/meta/llama-4-scout-17b-16e-instruct and @cf/openai/gpt-oss-120b did not.

#[CacheInstructions] from Laravel AI has no effect on this provider.

Custom headers

The package sends the headers array from the provider config. A configured header replaces a package header with the same name. Names match without regard to case. The package sends these headers on chat, streaming and embeddings.

'workers-ai' => [
    // ...
    'headers' => ['X-Trace-Id' => env('TRACE_ID')],
],

On laravel/ai 1.x, Provider::withHeaders() writes to the same array, so per-call headers reach Workers AI. Before package version 1.0.0, the package dropped the headers key and per-call headers.

Logging

AI Gateway logs are in the Cloudflare dashboard. The package does not write request logs. It writes one warning to the Laravel log when it raises the token cap for a thinking request.

Gateway retries

An AI Gateway with retry_max_attempts set retries inside your single HTTP request, and the package retries too. A late error can make the request several times longer, and your own timeout can cut it. Use one retry layer. If you use the gateway retry, set retry to false in the package config. See Errors and retries.

Type to search…

↑↓ navigate↵ selectEsc close