---
title: Send AWS Strands traces to Agent Analytics
description: "Export Python Strands Agents 1.60.0 OpenTelemetry spans to Agent Analytics, with session, user, and agent identity, and scrub content before it leaves your infrastructure."
product: general
lang: en
last_updated: 2026-10-14
token_estimate: 1748
---
# Send AWS Strands traces to Agent Analytics

> 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`.

The Python Strands Agents SDK emits OpenTelemetry GenAI spans. Point that exporter at Amplitude. You don't need the Amplitude AI SDK in the agent process.

This page covers Strands Agents 1.60.0. Amplitude stores the spans you export. Scrub personal data before it leaves your infrastructure. Refer to [Agent Analytics privacy modes](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.

```text
Connect this Python Strands agent to Amplitude Agent Analytics with native OpenTelemetry.

Fetch https://raw.githubusercontent.com/amplitude/Amplitude-AI-Node/main/docs/integrations/strands.md
and follow "Part 2: Coding agent procedure".
If @amplitude/ai is installed, the same file is under node_modules/@amplitude/ai/docs/integrations/.
Also follow https://raw.githubusercontent.com/amplitude/Amplitude-AI-Node/main/docs/integrations/privacy.md
and scrub content before export.
Follow links in the guide. If your source isn't listed, start at https://raw.githubusercontent.com/amplitude/Amplitude-AI-Node/main/docs/integrations/README.md
```

Review the changes, then verify with the steps in [Get started](#get-started).

## What this means for you

- **You run**: The Strands process, with its OpenTelemetry exporter pointed at Amplitude. A Collector is optional. Use one when you already collect gRPC spans or when you scrub attributes there.
- **You need**: `strands-agents[otel]==1.60.0`, your Amplitude project API key, and a stable conversation ID and product-analytics user ID on every turn.
- **Protocol**: OTLP over HTTP. Amplitude accepts protobuf and JSON. Amplitude doesn't accept gRPC. Bridge gRPC through a Collector that exports OTLP/HTTP.
- **Privacy**: Amplitude doesn't scrub content after receipt. Mask or drop the content attributes before export.

## Export

Set these variables in the agent process. In Python, encode the space in the header value as `%20`. Leave `Authorization=` as written.

```bash
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://api.amplitude.com/otlp/v1/traces
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20<project API key>"
OTEL_SERVICE_NAME=order-support
OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental,gen_ai_tool_definitions,gen_ai_span_attributes_only
```

EU data residency uses `https://api.eu.amplitude.com/otlp/v1/traces`.

`gen_ai_span_attributes_only` puts prompts, completions, and system instructions on span attributes. Amplitude doesn't read span events. Without that flag, identity, model, and token counts can still arrive, and the message text doesn't.

Call `StrandsTelemetry().setup_otlp_exporter()` before you construct `Agent()`. If a tracer provider is already installed, pass it in:

```python
from opentelemetry import trace
from strands.telemetry import StrandsTelemetry

StrandsTelemetry(tracer_provider=trace.get_tracer_provider()).setup_otlp_exporter()
```

## Identity

Pass one `trace_attributes` dictionary when you construct the agent. Strands copies it onto the model span and each tool span.

| Attribute | Value |
| --- | --- |
| `session.id` | The conversation, thread, or ticket ID. Use the same value on every turn. |
| `user.id` | The user ID product analytics already uses. Amplitude stores a missing user as `unknown`. |
| `gen_ai.agent.id` | A stable name for this agent. Amplitude doesn't read `gen_ai.agent.name`. |
| `gen_ai.provider.name` | The provider that served the model, such as `aws.bedrock`. |

Strands writes `gen_ai.provider.name=strands-agents` unless `trace_attributes` sets it afterward. Set the real provider. Amplitude records cost only when the span includes `gen_ai.usage.cost`.

## What carries over

| Strands span | Amplitude event |
| --- | --- |
| Model invocation (`chat`) | `[Agent] AI Response`, plus an `[Agent] User Message` for each new user message after the last assistant message |
| Tool (`execute_tool`) | `[Agent] Tool Call` |
| Agent and cycle spans | `[Agent] Span` |

A later turn repeats the earlier user text inside `gen_ai.input.messages`. Amplitude doesn't emit that earlier user message again. Tool output is `[Agent] Tool Output`. System instructions, when present, are `[Agent] System Prompt` on the AI Response.

## Session close

`[Agent] Session End` is optional and recommended. A native Strands export doesn't send it. 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. The first close is the one that counts. Enrichment, including the Session Record and content-based signals, runs once after that close. The record typically arrives 15 to 20 minutes after the ingestion batch processes the close, and can take longer during a backlog. Amplitude stores events that arrive after the close, and those events don't recompute that record.

When you know the unit of work is finished, send one `[Agent] Session End` over the HTTP API with the same `session.id` as `[Agent] Session ID`. A pure Strands export uses the server defaults. The OpenTelemetry translator doesn't read an idle-timeout attribute from a span. To change the idle window, send the HTTP `[Agent] Context` key `idle_timeout_minutes`, or set `[Agent] Session Idle Timeout Minutes` on that Session End event. Refer to [How sessions close](https://amplitude.com/docs/amplitude-ai/agent-analytics/sessions#how-sessions-close).

## When the app already runs the SDK

If the agent process already uses the Amplitude AI SDK, `AmplitudeAgentExporter` or `enableOtel()` can map spans in that process. Native OTLP above is the path when the SDK isn't loaded. SDK `redactPii` applies only on the in-process path.

## Get started

Follow the [Strands integration guide](https://github.com/amplitude/Amplitude-AI-Node/blob/main/docs/integrations/strands.md). Send two turns with the same `session.id`, then confirm in Live Events:

1. Session ID is `session.id`, not a trace ID.
2. User ID is the product-analytics user, not `unknown`.
3. Agent ID is `gen_ai.agent.id`.
4. The turn shows User Message text, the tool call, and AI Response text, with the model name and both token counts.
5. A canary of `ada@example.com` and `555-123-4567` is stored as `[email]` and `[phone]`. The raw values are absent.
6. The second turn doesn't duplicate the first user message.

A parenthesized US number keeps the opening parenthesis: `Call (555) 123-4567` becomes `Call ([phone]`.

Guide last verified on October 14, 2026, against `strands-agents` 1.60.0. Strands is a trademark of its owner. Amplitude isn't affiliated with or endorsed by that owner.

