For AI agents: a documentation index is available at /docs/llms.txt. Append .md to any page URL for markdown, or send Accept: text/markdown.
OpenTelemetry ingestion reference
This page is the reference for how Amplitude's OTLP endpoint translates OpenTelemetry GenAI spans into Agent Analytics [Agent] events. For setup steps, refer to Send OpenTelemetry traces to Agent Analytics.
Endpoints and protocol
| Region | Endpoint |
|---|---|
| US | https://api.amplitude.com/otlp/v1/traces |
| EU | https://api.eu.amplitude.com/otlp/v1/traces |
- Protocol: OTLP over HTTP only, as
http/protobuforhttp/json, with optional gzip. Amplitude doesn't support OTLP/gRPC. - Authentication: The project API key as a
Bearertoken or as anapi_keyquery parameter. Amplitude doesn't require a per-project enablement request. - Supported instrumentation: OpenTelemetry GenAI semantic conventions, OpenInference (
llm.*), OpenLLMetry and Traceloop (traceloop.*), OpenLIT, and LiteLLM. Amplitude ignores non-GenAI spans.
Attribute resolution order
Amplitude reads the first attribute present, in this order:
| Signal | Attributes Amplitude reads, in order |
|---|---|
| Session | gen_ai.conversation.id, session.id, traceloop.association.properties.session (or chat, thread, conversation), gen_ai.session.id, then resource session.id and gen_ai.conversation.id. With none of these, Amplitude falls back to the trace ID and marks the session degraded. Claude-native spans are the exception: anthropic.session.id is the session, and a different gen_ai.conversation.id is kept as the thread. |
| User | enduser.id, gen_ai.request.user, gen_ai.user, user.id, traceloop.association.properties.user, resource enduser.id and user.id, then LiteLLM's metadata.user_api_key_end_user_id. With none of these, the user resolves to unknown. |
| Agent | gen_ai.agent.id, else resource service.name. Claude-native spans don't fall back to service.name, so claude-platform isn't stored as the agent. |
| Parent agent | Span attribute amplitude.parent.agent.id only. A parent span ID and resource attributes aren't a parent agent. |
| Environment | deployment.environment, on the span or the resource. |
What maps to what
| OpenTelemetry source | [Agent] result |
|---|---|
gen_ai.operation.name of chat, text_completion, or generate_content | [Agent] AI Response, plus an [Agent] User Message synthesized from the new input messages |
gen_ai.operation.name of execute_tool | [Agent] Tool Call, with gen_ai.tool.name, gen_ai.tool.call.id, arguments, and result |
Any other operation, such as invoke_agent, retrieval, plan, or the memory operations | [Agent] Span |
gen_ai.request.model, gen_ai.response.model | [Agent] Model Name |
gen_ai.provider.name, or the deprecated gen_ai.system | [Agent] Provider |
gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, and the reasoning and cache token attributes | Token properties |
gen_ai.usage.cost | [Agent] Cost USD. This is an Amplitude extension: OpenTelemetry doesn't standardize cost, so most instrumentation omits it. |
gen_ai.response.finish_reasons | [Agent] Finish Reason |
gen_ai.input.messages, gen_ai.output.messages | $llm_message |
gen_ai.system_instructions, or a system-role message | [Agent] System Prompt on the AI Response |
Span status of ERROR | [Agent] Is Error, [Agent] Error Message |
trace_id, span_id, parent_span_id, start time, duration | [Agent] Trace ID, [Agent] Span ID, [Agent] Parent Span ID, event timestamp, [Agent] Latency Ms |
amplitude.parent.agent.id | [Agent] Parent Agent ID |
| Derived on the synthesized user message | [Agent] Message Source: explicit amplitude.message.source wins; otherwise agent when a parent agent is present, else user |
Amplitude doesn't calculate a cost when gen_ai.usage.cost is absent. Refer to How cost is calculated.
A chat call re-sends the whole prompt history, so only the user messages after the last assistant message become a new [Agent] User Message. Amplitude doesn't re-emit replayed history, and a tool-loop continuation whose new input is a tool result doesn't produce a user turn.
The AI SDK's in-process OpenTelemetry exporter uses its own mapping. Refer to OpenTelemetry attribute mapping in the SDK reference.
Claude Platform spans
Amplitude applies these rules only when the span name starts with anthropic. and the span also carries gen_ai.provider.name=anthropic, a com.anthropic.claude-platform or com.anthropic.sdk instrumentation scope, or resource service.name=claude-platform. A generic invoke_agent span, and an Anthropic chat span with any other name, stay on the mapping above.
| Claude span | [Agent] result |
|---|---|
anthropic.messages.create | User message and AI response. This span keeps the token total. |
anthropic.model_request under Messages API | [Agent] Span with usage removed, so the parent total isn't counted again. |
anthropic.session.turn with message text | User message and AI response, with no usage copied onto those events. |
anthropic.session.turn with no message text | [Agent] Span, with usage removed. |
anthropic.model_request with gen_ai.operation.name=chat | [Agent] Span that keeps model, tokens, cache, latency, and error. Several model requests each keep their own usage. |
Span name starting with anthropic.tool_use, anthropic.mcp_tool_use, or anthropic.custom_tool_use | [Agent] Tool Call. anthropic.tool_result.content is the tool output. anthropic.mcp.server.name is kept as an extra property. |
anthropic.test | Accepted and dropped. It doesn't appear in Live Events. |
anthropic.message.stop_reason, then anthropic.session.turn.stop_reason, becomes [Agent] Finish Reason on the AI Response when gen_ai.response.finish_reasons is absent. Amplitude doesn't invent enduser.id or gen_ai.usage.cost. Parent span IDs stay hierarchy. They aren't [Agent] Parent Agent ID.
To add the destination, refer to Send Claude Platform traces.
Delivery, limits, and responses
Delivery is at least once. Run a Collector with a persistent sending queue and keep retries enabled. An in-memory queue loses buffered spans on restart.
| Limit | Behavior |
|---|---|
| 4 MB decompressed request body | 413 |
| 2,000 translated events per export | 413. Lower your Collector batch size. |
| Response | Meaning |
|---|---|
200 | Accepted. A partial_success body reports any events that ingestion rejected. Watch for it, because a 200 tells your Collector to delete the batch. |
400 | Malformed request body or OTLP payload. Not retryable. |
401 | Missing or invalid API key. |
415 | A content type other than application/x-protobuf or application/json. |
503 | Temporary, including downstream ingestion outages. Your Collector should retry from its queue. |
The OTLP receiver never returns 403. Treat a 403 from another hop, such as a CDN or WAF, as a configuration error, not a missing Amplitude enablement.
Was this helpful?