# Send traces from the AI SDK

> Send traces of Vercel AI SDK calls, tool calls and agent steps.

This guide sends traces from a Node.js application that uses the Vercel AI SDK. Each `generateText`, `streamText` or agent call becomes a trace, with a span for each model call and tool call.

## Before you start

You need an API key with the `traces:write` scope. Create one under **Settings** in the dashboard, or run `div login`.

Install the OpenTelemetry SDK and the OTLP exporter:

```bash
npm install @opentelemetry/sdk-trace-node @opentelemetry/sdk-trace-base @opentelemetry/exporter-trace-otlp-http @opentelemetry/resources
```

Set the exporter's endpoint and key:

```bash
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.divergentlabs.xyz/api/otlp
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20$DIVERGENT_API_KEY"
export OTEL_SERVICE_NAME=my-app
```

Register a tracer provider at startup, before any AI SDK call:

```ts
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'

const provider = new NodeTracerProvider({
  spanProcessors: [new BatchSpanProcessor(new OTLPTraceExporter())],
})
provider.register()
```

In a Next.js app, `registerOTel` from `@vercel/otel` in `instrumentation.ts` does the same.

## AI SDK 7

1. Install the AI SDK's OpenTelemetry integration.

   ```bash
   npm install @ai-sdk/otel
   ```

2. Register it once, after the tracer provider. Every AI SDK call is then traced.

   ```ts
   import { registerTelemetry } from 'ai'
   import { OpenTelemetry } from '@ai-sdk/otel'

   registerTelemetry(new OpenTelemetry())
   ```

3. Name the call with `functionId`. The trace takes the name of its first span, `invoke_agent <model>`, and `functionId` is kept as `gen_ai.agent.name`.

   ```ts
   const result = await generateText({
     model,
     prompt: 'What is the weather in Amsterdam?',
     telemetry: { functionId: 'answer-weather' },
   })
   ```

To filter traces by session or user, run the call inside a span of your own that sets `session.id` and `user.id`. The AI SDK's spans become its children:

```ts
import { trace } from '@opentelemetry/api'

await trace.getTracer('my-app').startActiveSpan('answer question', async (span) => {
  span.setAttributes({ 'session.id': sessionId, 'user.id': userId })
  await generateText({ model, prompt })
  span.end()
})
```

## AI SDK 5 and 6

Enable telemetry on each call. `metadata.sessionId` and `metadata.userId` become the trace's session and user:

```ts
const result = await generateText({
  model,
  prompt: 'What is the weather in Amsterdam?',
  experimental_telemetry: {
    isEnabled: true,
    functionId: 'answer-weather',
    metadata: { sessionId, userId },
  },
})
```

AI SDK 7's `LegacyOpenTelemetry` integration writes the same attributes as AI SDK 5 and 6. Tracing reads both.

## Keep content out of traces

Set `recordInputs: false` and `recordOutputs: false` in `telemetry` (AI SDK 7) or `experimental_telemetry` (AI SDK 5 and 6). Spans then carry the model, tokens and timings, and no prompts or completions.

AI SDK 5, 6 and 7's `LegacyOpenTelemetry` record the headers passed to a call. Tracing drops them before a span is stored, except `content-type`, `content-length` and `user-agent`.

## Tokens

An AI SDK trace has an outer span for the call and a span for each model call inside it, and the outer span repeats the usage of the calls inside it. Tracing counts a trace's tokens from the model calls only, so a call that made two requests of 21 input tokens shows 42, not 84.
