> ## Documentation Index
> Fetch the complete documentation index at: https://glassray.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Stream AI traces from Vercel

> Connect your Vercel-deployed app to Glassray with a Trace Drain - three pasted values, no code changes to your exporter.

Vercel can stream your app's traces - including AI SDK telemetry - to Glassray via a [Trace Drain](https://vercel.com/docs/drains): Vercel POSTs standard OTLP/JSON to Glassray's ingest endpoint. Setup is copying three values into the Vercel dashboard.

<Note>
  Trace Drains require a Vercel **Pro or Enterprise** plan. On Hobby, use the [exporter env-var fallback](#hobby-plan-fallback) below - same endpoint, configured in your project instead of a drain.
</Note>

## Step 1 - Get your Glassray ingest details

In Glassray: **Settings → Sources → Add source → Vercel**. Glassray creates the source, mints a write-scoped ingest key (shown once), and displays the two values you'll paste into Vercel:

* the **endpoint URL** (`https://app.glassray.ai/api/public/otel/v1/traces`)
* one **custom header line** (`Authorization: Bearer glsk_…`)

<Tip>
  Create **one Glassray source per Vercel project** and scope the drain to that project - the ingest key routes every trace it receives into the source's Glassray project, so a 1:1 mapping keeps workspaces clean.
</Tip>

## Step 2 - Add a Trace Drain in Vercel

In the Vercel dashboard: **Team Settings → Drains → Add Drain**.

1. **Choose data to drain:** Traces
2. **Configure the drain:** name it, pick the Vercel project(s) to send
3. **Configure the destination** - stay on the **Custom Endpoint** tab:
   * **URL** → paste the Glassray endpoint
   * **Encoding** → **JSON** (not Protobuf - Glassray's endpoint is JSON-only and will reject protobuf with a `415`)
   * **Custom Headers** → toggle on, paste the `Authorization: Bearer glsk_…` line
   * **Signature Verification Secret** → leave as generated (not used by Glassray; auth rides the header)
   * Click **Test** - expect success - then **Create Drain**

<Tip>
  Drains bill per GB. Add a **path-prefix sampling rule** on the drain (e.g. only `/api/chat`) so framework and static-asset traffic never leaves Vercel - Glassray only needs the routes that run your AI code.
</Tip>

## Step 3 - Emit AI telemetry and verify

Register OpenTelemetry in your app's `instrumentation.ts`:

```ts instrumentation.ts theme={null}
import { registerOTel } from "@vercel/otel";

export function register() {
  registerOTel({ serviceName: "my-app" });
}
```

And enable telemetry on your AI SDK calls:

```ts theme={null}
const result = streamText({
  model,
  messages,
  experimental_telemetry: { isEnabled: true },
});
```

Deploy, trigger one AI request, and watch the source flip to **Connected** on the Sources page.

<Warning>
  **Keep AI routes on the Node runtime.** Spans created in the Edge runtime never reach Trace Drains - if your AI route sets `export const runtime = "edge"`, its AI spans will be missing. Remove the override (Node is the default) or move the AI calls to a Node route.
</Warning>

## What Glassray captures

A drained trace mixes Vercel's own spans (edge routing, function invocation, Next.js framework steps) with your app's AI spans. Glassray:

* reads the **`gen_ai.*`** semantic conventions and the AI SDK's **`ai.*`** telemetry attributes - model, tokens (including cache read/write), messages, tool calls, finish reason;
* computes **cost** from tokens × model price when the span doesn't carry an explicit cost;
* merges a trace that arrives **across several drain POSTs** (normal for Vercel) into one complete trace;
* names the trace from your app's request handler (e.g. `POST /api/chat/route`), not Vercel's infra spans;
* tags each trace with its Vercel **project, deployment, repo, and commit SHA** from the drain's resource attributes.

Filter rules for a Vercel source should use **"any span" (descendant) scope** - the trace root is Vercel's own infra span, so root-scoped rules are unreliable; Vercel-side path sampling plus a descendant keep-rule is the recommended combination.

## Hobby plan fallback

Without a drain, point the AI SDK / OTel exporter at the same endpoint via project env vars (Project → Settings → Environment Variables):

```bash theme={null}
OTEL_EXPORTER_OTLP_ENDPOINT="https://app.glassray.ai/api/public/otel"
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <your ingest key>"
OTEL_EXPORTER_OTLP_PROTOCOL="http/json"
```

See [Send traces via OpenTelemetry](/docs/otlp-ingestion) for the full exporter reference.

## Troubleshooting

* **Test button fails** - check the URL has no trailing spaces, Encoding is JSON, and the header line is exactly `Authorization: Bearer glsk_…` (one line, no quotes).
* **Drain created but no traces** - confirm the drain includes your Vercel project, telemetry is registered (`instrumentation.ts`), and the AI route runs on the **Node runtime**.
* **Traces arrive but no AI spans** - enable `experimental_telemetry` on your AI SDK calls (or add `@ai-sdk/otel`); plain HTTP spans alone mean the AI instrumentation isn't emitting.
* **Using Sentry v8+?** Its SDK takes over OTel setup by default and can swallow spans - set `skipOpenTelemetrySetup: true` in `Sentry.init` and keep `@vercel/otel` as the registration point.
* **Very large prompts** - Vercel truncates span attributes over \~1 MB (the trace shows a `truncated` tag in Glassray) and drops spans over the cap entirely.
