---
title: Send OpenTelemetry traces to Agent Analytics
description: "Point an OpenTelemetry exporter or Collector at Amplitude's OTLP endpoints so GenAI spans and agent log events become Agent Analytics sessions, with no Amplitude SDK or re-instrumentation."
product: general
lang: en
last_updated: 2026-11-09
token_estimate: 3229
---
# Send OpenTelemetry traces to Agent Analytics

Amplitude's OTLP endpoints accept OpenTelemetry GenAI spans and agent log events and translate them into the same `[Agent]` events the AI SDK produces, so sessions, enrichment, and every chart work the same way. If your stack already emits GenAI spans or logs, point your exporter at Amplitude. You don't need the Amplitude SDK or new instrumentation.

> For AI agents: a documentation index is available at [/docs/llms.txt](/docs/llms.txt). Append `.md` to any page URL for markdown, or send `Accept: text/markdown`.

Use this path when you already run an OpenTelemetry exporter or Collector. If you instrument a Node or Python app with the AI SDK instead, use the SDK's [in-process OTel exporters](https://amplitude.com/docs/sdks/agent-analytics/sdk#ingest-opentelemetry-spans), which are a separate path. Before you start, decide your [session ID](https://amplitude.com/docs/amplitude-ai/agent-analytics/sessions) and [privacy mode](https://amplitude.com/docs/amplitude-ai/agent-analytics/privacy-modes).

## Set up with your AI coding agent (recommended)

Paste this prompt into your AI coding agent, such as Cursor, Claude Code, Windsurf, GitHub Copilot, or Codex. The agent finds the OpenTelemetry exporter or Collector config that your app already uses and points it at Amplitude. It doesn't add SDK code or change your spans.

```text
Send this app's OpenTelemetry GenAI traces to Amplitude Agent Analytics.

Find the OpenTelemetry exporter or Collector config that this app already uses. Don't add the Amplitude SDK, and don't re-instrument any code.

Set these environment variables on that exporter. Use the EU endpoint instead if this project is in EU residency: https://api.eu.amplitude.com/otlp/v1/traces

OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://api.amplitude.com/otlp/v1/traces
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <project API key>"
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf

Read the project API key from an environment variable or secret manager. Don't hardcode it. Then confirm the exporter sends GenAI spans (gen_ai.* attributes), and report what you changed.
```

Review the changes it proposes, then [verify your instrumentation](https://amplitude.com/docs/amplitude-ai/agent-analytics/verify).

## Configure your exporter

Set these environment variables on your exporter or Collector:

```bash
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://api.amplitude.com/otlp/v1/traces   # US
# OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://api.eu.amplitude.com/otlp/v1/traces  # EU
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <project API key>"
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf   # or http/json
```

The OTLP HTTP traces endpoint is generally available in the US and EU. For logs, refer to [Send OpenTelemetry logs](#send-opentelemetry-logs). A valid project API key is enough. Amplitude doesn't require a per-project enablement request, and the receiver never returns `403` for a missing allowlist.

- **Protocol**: OTLP over HTTP only. Amplitude accepts `http/protobuf` (the OpenTelemetry SDK and Collector default) and `http/json`, with optional gzip. Amplitude doesn't support OTLP/gRPC, so bridge gRPC traffic through a local Collector with an OTLP/HTTP exporter.
- **Authentication**: Pass the project API key as a `Bearer` token or as an `api_key` query parameter.
- **Supported instrumentation**: OpenTelemetry GenAI semantic conventions, OpenInference (`llm.*`), OpenLLMetry and Traceloop (`traceloop.*`), OpenLIT, and LiteLLM. Amplitude ignores non-GenAI spans, so you can point a mixed Collector at Amplitude.

## Send OpenTelemetry logs

Some agents put the conversation on OpenTelemetry logs instead of spans. Claude Code is one. Amplitude accepts OTLP/HTTP log records at a second endpoint with the same key and protocol:

```bash
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://api.amplitude.com/otlp/v1/logs   # US
# OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://api.eu.amplitude.com/otlp/v1/logs  # EU
OTEL_LOGS_EXPORTER=otlp
```

- **Allowlist**: Only Claude Code agent events and the GenAI log events (`gen_ai.user.message`, `gen_ai.assistant.message`, `gen_ai.choice`, and `gen_ai.tool.message`) become `[Agent]` events. Amplitude drops every other log record, so a mixed logs pipeline is safe.
- **Identity**: A record needs a session ID and a user ID that joins your data, or Amplitude skips it. Set `OTEL_RESOURCE_ATTRIBUTES=enduser.id=<user ID>` to choose the user.
- **One canonical signal**: Claude Code turns come from logs, and its spans add detail only. For every other source, spans carry the turn, and GenAI log events add text to it. Exporting both doesn't double-count tokens or cost.

For the full mapping and identity rules, refer to [Log records](https://amplitude.com/docs/amplitude-ai/agent-analytics/otlp-reference#log-records). For Claude Code setup, refer to [Claude Code](https://amplitude.com/docs/amplitude-ai/agent-analytics/integrations/claude-code).

## Send Claude Platform traces

Claude already exports OpenTelemetry. Add Amplitude as a Claude destination. You don't need the Amplitude SDK for this path.

In the Claude console, set the destination base URL to the regional host. Claude appends `/v1/traces`, so the base URL doesn't include that path.

| Region | Console base URL | Advanced traces endpoint |
| --- | --- | --- |
| US | `https://api.amplitude.com/otlp` | `https://api.amplitude.com/otlp/v1/traces` |
| EU | `https://api.eu.amplitude.com/otlp` | `https://api.eu.amplitude.com/otlp/v1/traces` |

Set the header `Authorization: Bearer <project API key>`.

- **Full content** sends transcripts. User and assistant text on a Managed Agents turn become `[Agent] User Message` and `[Agent] AI Response`.
- **Metadata only** still sends model, tokens, latency, tool calls, and errors. A turn with no message text stays an `[Agent] Span`.
- Use one export path. If the Claude SDK and Claude Platform both export the same response, Amplitude stores both.
- The destination **Test** action sends `anthropic.test`. Amplitude accepts that span and doesn't show it in Live Events.

A Claude session is `anthropic.session.id`. `gen_ai.conversation.id` stays the thread inside that session. Messages API spans have no Claude session id, so they use the [session attribute order](https://amplitude.com/docs/amplitude-ai/agent-analytics/otlp-reference#attribute-resolution-order). For the span mapping, refer to [Claude Platform spans](https://amplitude.com/docs/amplitude-ai/agent-analytics/otlp-reference#claude-platform-spans).

## Set session and user attributes

The session attribute is the one change worth making to your instrumentation. Amplitude maps everything else automatically.

An Amplitude agent session is a whole conversation, not one trace. Without a conversation attribute, every trace becomes its own single-turn session. Task completion and abandonment then read as noise, and because billing is per agent session, one conversation bills as several.

Set these attributes on your GenAI spans:

- **Session**: `gen_ai.conversation.id`, set to your conversation, ticket, or run ID. Refer to [Choose a session ID](https://amplitude.com/docs/amplitude-ai/agent-analytics/sessions#choose-a-session-id).
- **User**: `enduser.id`, set to the same user ID you use for product analytics. Never send a placeholder such as `anonymous`. Refer to [Set user IDs](https://amplitude.com/docs/amplitude-ai/agent-analytics/sessions#set-user-ids).
- **Agent**: `gen_ai.agent.id`, or Amplitude falls back to the resource `service.name`.
- **Parent agent**: the span attribute `amplitude.parent.agent.id`. A parent span ID isn't a parent agent, and Amplitude doesn't read a parent agent from the resource.

Amplitude also reads other common attributes, such as `session.id` and Traceloop association properties. For the full resolution order, refer to [Attribute resolution order](https://amplitude.com/docs/amplitude-ai/agent-analytics/otlp-reference#attribute-resolution-order).

### Record calls from other agents

When another agent calls yours, verify its agent context before you trust any ID it sends. Then record the outcome with these span attributes:

| Span attribute | Set it to |
| --- | --- |
| `amplitude.peer.agent.id`, `amplitude.peer.session.id`, `amplitude.peer.trace.id` | The caller's agent, session, and trace ID, when its context isn't trusted. Keep your own session ID and user. |
| `amplitude.context.verify.result` | The verify result, such as `ok`, `unsigned`, or `bad_signature`. Amplitude derives `[Agent] Context Trust` from it. |
| `amplitude.context.trust` | `none` when the call carried no agent context. With a verify result, Amplitude ignores this attribute. |
| `amplitude.actor.type` | `agent` or `human`. |
| `amplitude.identity.type` | `account`, `pairwise`, or `none`. |

The values and verification rules come from [Amplitude agent context v1](https://amplitude.com/docs/amplitude-ai/agent-analytics/agent-context-v1). For the mapping, refer to [Agent context attributes](https://amplitude.com/docs/amplitude-ai/agent-analytics/otlp-reference#agent-context-attributes).

> **Warning:** Don't trust inbound baggage
>
> A baggage span processor copies inbound W3C `baggage` into span attributes, so a caller outside your trust boundary could set your session or user. Don't copy inbound baggage keys for `session.id`, `user.id`, `enduser.id`, `gen_ai.conversation.id`, or any `amplitude.*` attribute from untrusted callers.

To report cost, also set `gen_ai.usage.cost`. Amplitude doesn't calculate a cost when this attribute is absent.

To record the A/B test or feature flag variant a span ran under, add a `feature_flag.evaluation` span event, or set `feature_flag.key` and `feature_flag.result.variant` on the span. Every event the span produces then carries `[Agent] Experiments` as `key:variant` strings, plus the single-experiment properties when one evaluation applies. You don't need Amplitude-specific code; the standard OpenTelemetry feature-flag conventions are enough. Refer to [Feature flag attributes](https://amplitude.com/docs/amplitude-ai/agent-analytics/otlp-reference#feature-flag-attributes).

## Apply your privacy mode

Most OpenTelemetry GenAI instrumentation treats message content as opt-in.

- **Content capture off**: You get the `metadata_only` shape: cost, tokens, latency, model, and sessions, with no transcripts.
- **Content capture on**: `gen_ai.input.messages`, `gen_ai.output.messages`, and `gen_ai.system_instructions` become message text and `[Agent] System Prompt`. Amplitude stores that text as exported. Amplitude doesn't scrub it after receipt. Scrub those attributes, plus tool arguments and tool results, before export. Refer to [Agent Analytics privacy modes](https://amplitude.com/docs/amplitude-ai/agent-analytics/privacy-modes).

Redaction that must happen before data leaves your network belongs in your application or a Collector processor. For what each mode sends, refer to [Agent Analytics privacy modes](https://amplitude.com/docs/amplitude-ai/agent-analytics/privacy-modes).

## Session close on an OTLP-only path

`[Agent] Session End` is optional and recommended. An OpenTelemetry export doesn't send it unless your pipeline adds that event. Amplitude closes the session after 30 minutes without a qualifying agent event by default, and 24 hours from the session start when no per-session override is set. Enrichment runs once after that first close. The OpenTelemetry translator doesn't read an idle-timeout attribute from a span. To change the window, send the HTTP `[Agent] Context` key `idle_timeout_minutes`, or send one `[Agent] Session End` with `[Agent] Session Idle Timeout Minutes`. Refer to [How sessions close](https://amplitude.com/docs/amplitude-ai/agent-analytics/sessions#how-sessions-close). The agent framework guides use this same path. Refer to [Agent frameworks](https://amplitude.com/docs/amplitude-ai/agent-analytics/integrations#agent-frameworks).

## Keep delivery reliable

Delivery is at least once. Run a Collector with a persistent sending queue and keep retries enabled, because an in-memory queue loses buffered spans on restart. A `200` response can carry a `partial_success` body listing rejected events. For limits and response codes, refer to [Delivery, limits, and responses](https://amplitude.com/docs/amplitude-ai/agent-analytics/otlp-reference#delivery-limits-and-responses).

## Verify

Send one traced conversation, then confirm in Live Events that the events arrive with:

1. A real `[Agent] Session ID`, not a trace ID.
2. A populated `[Agent] Agent ID`.
3. Your own user IDs, not `unknown`.

Those three values come from the attributes in [Set session and user attributes](#set-session-and-user-attributes). For the full checklist, refer to [Verify your Agent Analytics instrumentation](https://amplitude.com/docs/amplitude-ai/agent-analytics/verify).

