> ## Documentation Index
> Fetch the complete documentation index at: https://arizeai-433a7140.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Cloudflare AI Gateway Tracing

> Configure Cloudflare AI Gateway to send traces to Phoenix by exporting the gateway's OpenTelemetry spans or by instrumenting your app with OpenInference.

Configure Cloudflare AI Gateway to send traces to Phoenix, either by exporting the gateway's spans
(no application code) or by instrumenting your application with OpenInference.

## Prerequisites

* A [Cloudflare AI Gateway](https://developers.cloudflare.com/ai-gateway/). Create one in the
  Cloudflare dashboard under **AI → AI Gateway**.
* A running Phoenix instance (see [Launch Phoenix](#launch-phoenix)). To export gateway spans,
  Cloudflare must be able to reach your Phoenix collector.
* Python 3.10+ (only to instrument your application).

## Launch Phoenix

> **Local**
>
> ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
> uvx arize-phoenix serve
> ```
>
> No [uv](https://docs.astral.sh/uv/)? `pip install arize-phoenix && phoenix serve` does the same thing. See [Terminal setup](/docs/phoenix/environments.md#terminal) for customization.
>
> **Container**
>
> ```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
> docker run -p 6006:6006 -p 4317:4317 arizephoenix/phoenix:latest
> ```
>
> Images are published to [Docker Hub](https://hub.docker.com/r/arizephoenix/phoenix). See [Docker](/docs/phoenix/self-hosting/deployment-options/docker.md) for volumes, PostgreSQL, and other options.
>
> **Self-Host**
>
> Run Phoenix on your own infrastructure, backed by PostgreSQL so traces persist beyond a single process. This is the option to reach for once Phoenix is shared across a team or environment.
>
> The [self-hosting guide](/docs/phoenix/self-hosting.md) covers [Kubernetes](/docs/phoenix/self-hosting/deployment-options/kubernetes.md), [Helm](/docs/phoenix/self-hosting/deployment-options/kubernetes-helm.md), [Railway](/docs/phoenix/self-hosting/deployment-options/railway.md), [AWS CloudFormation](/docs/phoenix/self-hosting/deployment-options/aws-with-cloudformation.md), [Google Cloud Run](/docs/phoenix/self-hosting/deployment-options/google-cloud-run.md), [Azure](/docs/phoenix/self-hosting/deployment-options/azure.md), and [Render](/docs/phoenix/self-hosting/deployment-options/render.md), plus authentication and configuration.
>
> Phoenix serves its UI and OTLP HTTP on port **6006**, and OTLP gRPC on port **4317**. For a local instance that's [http://localhost:6006](http://localhost:6006). Leave it running while you work.

> [!NOTE]
>
> To export gateway spans, AI Gateway sends them from Cloudflare's edge, so Cloudflare must be able
> to reach your Phoenix collector. A deployed Phoenix endpoint can receive spans directly. To use a
> local endpoint, expose it with a tunnel such as [cloudflared](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/).

## Set up tracing

**Export gateway spans**

This option does not require application code. The gateway exports a span for each request it
proxies.

In the Cloudflare dashboard, open your gateway, go to **Settings → Otel Integration → Add
Destination**, and enter these values:

1. **OTLP Traces Endpoint**: your Phoenix endpoint with `/v1/traces` appended (for a local instance
   exposed through a tunnel, use `https://<your-tunnel-hostname>/v1/traces`).
2. **Content Type**: select **Protobuf**.
3. **Custom Headers**: add `x-project-name` set to the Phoenix project you want traces to land in.
   Phoenix creates the project on the first span.

> [!WARNING]
>
> Set **Content Type** to **Protobuf**, not the default **JSON**. Phoenix's trace endpoint accepts
> only Protobuf, and a destination left on JSON is rejected before any spans arrive.

> [!NOTE]
>
> If your Phoenix requires an API key, the destination also needs an `Authorization` value of
> `Bearer <your-phoenix-api-key>`. Cloudflare can send this value as a header or read it from a
> Secrets Store attached to the gateway. Some gateways reject a plain header and require the Secrets
> Store. Use Cloudflare's [OTel integration docs](https://developers.cloudflare.com/ai-gateway/observability/otel-integration/)
> for the current setup steps.

Save the destination, then send traffic through the gateway. Cloudflare exports spans in batches, so
wait a minute or two for them to appear.

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
curl "https://gateway.ai.cloudflare.com/v1/<account-id>/<gateway-name>/openai/chat/completions" \
  --header "Authorization: Bearer <provider-api-key>" \
  --header "Content-Type: application/json" \
  --data '{"model":"gpt-5-mini","messages":[{"role":"user","content":"Hello from Cloudflare AI Gateway"}]}'
```

**Instrument your app**

Trace the OpenAI client in your application, and route requests through the gateway's
OpenAI-compatible endpoint. Phoenix captures full input and output messages, so prompts and
responses render in Phoenix. The `/compat` endpoint lets the OpenAI SDK reach any provider the
gateway supports by setting `model` to `provider/model` (for example `openai/gpt-5-mini` or
`workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast`).

Install the packages:

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
pip install arize-phoenix-otel openinference-instrumentation-openai openai
```

Set your environment variables:

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
export PHOENIX_COLLECTOR_ENDPOINT="<your-phoenix-collector-endpoint>"  # use http://localhost:6006 for local Phoenix
export PHOENIX_API_KEY="<your-phoenix-api-key>"          # required for Phoenix Cloud or authenticated Phoenix
export OPENAI_API_KEY="<your-provider-api-key>"
export CLOUDFLARE_ACCOUNT_ID="<your-cloudflare-account-id>"
export CLOUDFLARE_AI_GATEWAY_NAME="<your-gateway-name>"
```

Register Phoenix and instrument the OpenAI client:

```python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import os
from phoenix.otel import register
from openinference.instrumentation.openai import OpenAIInstrumentor
from openai import OpenAI

tracer_provider = register(project_name="cloudflare-ai-gateway")
OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)

client = OpenAI(
    base_url=(
        "https://gateway.ai.cloudflare.com/v1/"
        f"{os.environ['CLOUDFLARE_ACCOUNT_ID']}/"
        f"{os.environ['CLOUDFLARE_AI_GATEWAY_NAME']}/compat"
    ),
    api_key=os.environ["OPENAI_API_KEY"],
)

response = client.chat.completions.create(
    model="openai/gpt-5-mini",
    messages=[{"role": "user", "content": "Hello from Cloudflare AI Gateway"}],
)
```

`register()` reads `PHOENIX_COLLECTOR_ENDPOINT` and `PHOENIX_API_KEY` from the environment. For a
local Phoenix instance, no other Phoenix configuration is needed.

> [!NOTE]
>
> If your gateway has Authenticated Gateway enabled, also pass its token when you create the client,
> for example `default_headers={"cf-aig-authorization": "Bearer <gateway-token>"}` in the
> `OpenAI(...)` call.

## Link gateway spans to your app's traces

When you export gateway spans, you can nest the gateway span under an existing application span
instead of starting a new trace. Pass the parent context to the gateway request with these headers:

| Header | Value |
| - | - |
| `cf-aig-otel-trace-id` | the active trace ID (32-character hex string) |
| `cf-aig-otel-parent-span-id` | the parent span ID to attach to (16-character hex string) |

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
curl "https://gateway.ai.cloudflare.com/v1/<account-id>/<gateway-name>/openai/chat/completions" \
  --header 'cf-aig-otel-trace-id: a1b2c3d4e5f60718293a4b5c6d7e8f90' \
  --header 'cf-aig-otel-parent-span-id: 1122334455667788' \
  --header 'Content-Type: application/json' \
  --data '{"model":"gpt-5-mini","messages":[{"role":"user","content":"Hello"}]}'
```

## Add custom metadata

Attach your own key/value metadata to gateway requests with the `cf-aig-metadata` header (JSON).
Cloudflare exports each key and value as a span attribute in Phoenix, so you can filter and group by
metadata such as user, team, environment, or tenant.

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
--header 'cf-aig-metadata: {"environment":"production","team":"platform"}'
```

Do not put secrets or sensitive personal data in metadata. Phoenix stores these values as span
attributes visible to anyone with access to the project.

## What renders in Phoenix

* **Gateway export:** each proxied request is an **LLM span** with the model name and token counts
  populated. The provider, cost, and full prompt and response are stored under the span's
  **Attributes**.
* **App instrumentation (OpenInference):** spans include the model, token counts, and the prompt and
  response rendered in the formatted message view.

## Troubleshooting

* **No spans arriving (gateway export):** confirm **Content Type** is **Protobuf** (JSON is
  rejected), the endpoint ends in `/v1/traces`, and Cloudflare can reach your Phoenix collector.
* **Spans in the wrong project:** set the `x-project-name` header (gateway export) or `project_name`
  in `register()` (app instrumentation).
* **Nothing yet:** Cloudflare exports in batches, so send a few requests and wait a minute or two.

## Resources

> **[Cloudflare AI Gateway OTEL docs](https://developers.cloudflare.com/ai-gateway/observability/otel-integration/)**
>
> Cloudflare's collector configuration reference

> **[OpenTelemetry Gen AI semantics](https://opentelemetry.io/docs/specs/semconv/gen-ai/)**
>
> The conventions AI Gateway emits
