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

# Send LLM and AI Agent Traces with OpenTelemetry

> Instrument an LLM application or AI agent with an OpenTelemetry-compatible library or framework, such as OpenLLMetry, OpenInference, Strands Agents, Cloudflare Agents, or the Claude SDK, and send its traces to a Mezmo Pipeline.

You can send traces from LLM applications and AI agents to a Mezmo Pipeline with OpenTelemetry (OTel). Each span records one step, such as a model call, an agent turn, or a tool use, with its latency, token usage, and IDs. After the spans reach your Pipeline, you can filter, sample, and enrich them, then route them to the Destinations you already use.

Mezmo receives these spans through the [OpenTelemetry Source](https://docs.mezmo.com/telemetry-pipelines/open-telemetry-source.md), the same OTLP endpoint you use for any other trace. Any library or framework that exports spans over OTLP/HTTP can send them, whichever LLM provider it calls.

## Before You Begin

You need:

* A Pipeline with an [OpenTelemetry Source](https://docs.mezmo.com/telemetry-pipelines/open-telemetry-source.md). Connect the Source's **Traces** output to the Processors and Destinations that should receive your spans.
* A Mezmo [ingestion key](https://docs.mezmo.com/docs/ingestion-key.md#ingestion-keys). In the Mezmo Web App, go to **Settings > Organization > API Keys**.
* An application that emits OpenTelemetry spans for its LLM calls, through an instrumentation library, a framework with built-in tracing, or your own code. See [Choose an Instrumentation Library](#choose-an-instrumentation-library).

Every sender uses these connection settings:

| Setting | Value |
| - | - |
| OTLP endpoint | `https://logs.mezmo.com/otel` |
| Protocol | OTLP over HTTP, with protobuf or JSON encoding. The endpoint doesn't accept gRPC. |
| Authentication header | `apikey`, set to your ingestion key |

> [!NOTE]
>
> Settings that take a base endpoint, such as `OTEL_EXPORTER_OTLP_ENDPOINT` or the Collector's `otlphttp` exporter, use `https://logs.mezmo.com/otel` without a path, because the exporter adds `/v1/traces` for you. Settings that take the full traces URL, such as `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`, a Spring Boot `management.otlp.tracing.endpoint`, or a Cloudflare destination, use `https://logs.mezmo.com/otel/v1/traces`.

## Choose an Instrumentation Library

LLM tracing libraries name span attributes according to one of several semantic conventions. The convention decides which attribute names reach your Pipeline, so note which one your library uses before you configure Processors:

| Convention | How to recognize its spans |
| - | - |
| [OpenTelemetry GenAI](https://opentelemetry.io/docs/specs/semconv/gen-ai/) | Attributes start with `gen_ai.`, for example `gen_ai.request.model` and `gen_ai.usage.input_tokens`. |
| [OpenLLMetry](https://github.com/traceloop/openllmetry) | GenAI attributes, plus indexed prompt attributes such as `gen_ai.prompt.0.content` and `llm.` attributes such as `llm.usage.total_tokens`. |
| [OpenInference](https://arize-ai.github.io/openinference/spec/semantic_conventions.html) | The `openinference.span.kind` attribute, with attributes such as `llm.model_name` and `llm.token_count.prompt`. |
| [Langfuse](https://langfuse.com/integrations/native/opentelemetry) | Attributes start with `langfuse.`, for example `langfuse.observation.type`. |

These libraries and frameworks emit OpenTelemetry spans for LLM calls and agent activity. Each one has its own setup, so follow its documentation for installation and options, and use the [exporter settings](#configure-the-opentelemetry-exporter) on this page to send its spans to Mezmo.

> [tab] Python
>
> | Framework or provider | Instrumentation | Convention |
> | - | - | - |
> | [OpenAI](https://platform.openai.com/docs/api-reference/introduction) | [`opentelemetry-instrumentation-openai-v2`](https://pypi.org/project/opentelemetry-instrumentation-openai-v2/) | GenAI |
> | [OpenAI](https://platform.openai.com/docs/api-reference/introduction) | [`openinference-instrumentation-openai`](https://arize-ai.github.io/openinference/python/instrumentation/openinference-instrumentation-openai/) | OpenInference |
> | [Anthropic](https://docs.anthropic.com/en/api/) | [`opentelemetry-instrumentation-anthropic`](https://pypi.org/project/opentelemetry-instrumentation-anthropic/) | OpenLLMetry |
> | [Google GenAI](https://ai.google.dev/gemini-api/docs) | [`opentelemetry-instrumentation-google-genai`](https://pypi.org/project/opentelemetry-instrumentation-google-genai/) | GenAI |
> | [Google Vertex AI](https://cloud.google.com/vertex-ai/generative-ai/docs/overview) | [`opentelemetry-instrumentation-vertexai`](https://pypi.org/project/opentelemetry-instrumentation-vertexai/) | GenAI |
> | [AWS Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/) | [`opentelemetry-instrumentation-botocore`](https://pypi.org/project/opentelemetry-instrumentation-botocore/) | GenAI |
> | [LangChain](https://python.langchain.com/docs/introduction/) | [`opentelemetry-instrumentation-langchain`](https://pypi.org/project/opentelemetry-instrumentation-langchain/) | OpenLLMetry |
> | [LlamaIndex](https://docs.llamaindex.ai/) | [`opentelemetry-instrumentation-llamaindex`](https://pypi.org/project/opentelemetry-instrumentation-llamaindex/) | OpenLLMetry |
> | Many providers and frameworks | [OpenLLMetry](https://www.traceloop.com/openllmetry) ([`traceloop-sdk`](https://pypi.org/project/traceloop-sdk/)) | OpenLLMetry |
> | [Strands Agents](https://strandsagents.com/latest/) | Built in. See [Trace Strands Agents](#trace-strands-agents). | GenAI |
> | [Langfuse](https://langfuse.com/integrations/native/opentelemetry) | Built in to the Langfuse SDK. See [Send Langfuse Spans to Mezmo](#send-langfuse-spans-to-mezmo). | Langfuse |
> | [Claude Platform](https://platform.claude.com/docs) | Built in. See [Trace Claude SDK Calls](#trace-claude-sdk-calls) and [Trace Claude API and Managed Agents Activity](#trace-claude-api-and-managed-agents-activity). | GenAI |

> [tab] Node.js
>
> | Framework or provider | Instrumentation | Convention |
> | - | - | - |
> | [OpenAI](https://platform.openai.com/docs/api-reference/introduction) | [`@opentelemetry/instrumentation-openai`](https://www.npmjs.com/package/@opentelemetry/instrumentation-openai) | GenAI |
> | [Cloudflare Agents](https://developers.cloudflare.com/agents/) | Built in, through Workers Observability. See [Trace Cloudflare Agents](#trace-cloudflare-agents). | GenAI |

> [tab] Java
>
> | Framework or provider | Instrumentation | Convention |
> | - | - | - |
> | [Spring AI](https://docs.spring.io/spring-ai/reference/) | Built in, through [Micrometer](https://micrometer.io/) with the OpenTelemetry tracing bridge | GenAI |
> | [LangChain4j](https://docs.langchain4j.dev/) | Built in, through its OpenTelemetry module | GenAI |
> | [AWS Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/) | [OpenTelemetry Java agent](https://opentelemetry.io/docs/zero-code/java/agent/) | GenAI |

Other libraries that export OTLP over HTTP also work. When no library covers your provider, [add the GenAI attributes to your own spans](#add-genai-attributes-to-your-own-spans).

## Configure the OpenTelemetry Exporter

The OpenTelemetry SDK reads its exporter settings from standard environment variables, so most applications can send traces to Mezmo without code changes to the exporter. Set these variables in the environment where your application runs:

```bash theme={null}
export OTEL_SERVICE_NAME="support-agent"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_ENDPOINT="https://logs.mezmo.com/otel"
export OTEL_EXPORTER_OTLP_HEADERS="apikey=<YOUR_INGESTION_KEY>"

# Emit the latest GenAI semantic conventions from instrumentation libraries that support them.
export OTEL_SEMCONV_STABILITY_OPT_IN="gen_ai_latest_experimental"
```

* `OTEL_SERVICE_NAME` becomes the `service.name` resource attribute. Mezmo uses it to set the `app` annotation on each span, so pick a name that identifies the application or agent.
* `OTEL_SEMCONV_STABILITY_OPT_IN` affects only instrumentation libraries that read it. Some libraries emit older GenAI attribute names unless you set it. If you already set this variable for other features, add `gen_ai_latest_experimental` to its comma-separated value.

If your services already send telemetry to an [OpenTelemetry Collector](https://docs.mezmo.com/telemetry-pipelines/otel-collector.md) that exports to Mezmo, point the application at the Collector's OTLP receiver instead, and let the Collector forward the spans.

## Send Traces from Your Application

### Instrument an LLM Client Library

Instrumentation libraries patch an LLM client so that each call produces a span, without changes to the code that makes the call. These examples trace OpenAI calls in Python. The same pattern works for the other providers and frameworks that each library supports.

1. Set the environment variables from [Configure the OpenTelemetry Exporter](#configure-the-opentelemetry-exporter).
2. Install the libraries and enable the instrumentation before your first model call:

> [tab] OpenTelemetry
>
> ```bash theme={null}
> pip install openai opentelemetry-sdk opentelemetry-exporter-otlp-proto-http opentelemetry-instrumentation-openai-v2
> ```
>
> ```python theme={null}
> from openai import OpenAI
> from opentelemetry import trace
> from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
> from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor
> from opentelemetry.sdk.trace import TracerProvider
> from opentelemetry.sdk.trace.export import BatchSpanProcessor
>
> provider = TracerProvider()
> provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
> trace.set_tracer_provider(provider)
>
> OpenAIInstrumentor().instrument()
>
> client = OpenAI()
> client.chat.completions.create(
>     model="gpt-4o",
>     messages=[{"role": "user", "content": "Summarize today's error spikes."}],
> )
>
> provider.force_flush()
> ```
>
> This library records prompts and responses only when you set `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true`.

> [tab] OpenInference
>
> ```bash theme={null}
> pip install openai opentelemetry-sdk opentelemetry-exporter-otlp-proto-http openinference-instrumentation-openai
> ```
>
> ```python theme={null}
> from openai import OpenAI
> from openinference.instrumentation.openai import OpenAIInstrumentor
> from opentelemetry import trace
> from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
> from opentelemetry.sdk.trace import TracerProvider
> from opentelemetry.sdk.trace.export import BatchSpanProcessor
>
> provider = TracerProvider()
> provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
> trace.set_tracer_provider(provider)
>
> OpenAIInstrumentor().instrument(tracer_provider=provider)
>
> client = OpenAI()
> client.chat.completions.create(
>     model="gpt-4o",
>     messages=[{"role": "user", "content": "Summarize today's error spikes."}],
> )
>
> provider.force_flush()
> ```

> [tab] OpenLLMetry
>
> The Traceloop SDK sets up the tracer provider and instruments every supported library it finds, so you configure it with its own variables instead of the `OTEL_EXPORTER_OTLP_*` variables:
>
> ```bash theme={null}
> pip install openai traceloop-sdk
> export TRACELOOP_BASE_URL="https://logs.mezmo.com/otel"
> export TRACELOOP_HEADERS="apikey=<YOUR_INGESTION_KEY>"
> ```
>
> ```python theme={null}
> from openai import OpenAI
> from traceloop.sdk import Traceloop
>
> Traceloop.init(app_name="support-agent", disable_batch=True)
>
> client = OpenAI()
> client.chat.completions.create(
>     model="gpt-4o",
>     messages=[{"role": "user", "content": "Summarize today's error spikes."}],
> )
> ```
>
> `disable_batch=True` sends each span right away, which suits short scripts. Leave it out for long-running services. See [OpenLLMetry configuration](https://www.traceloop.com/docs/openllmetry/configuration) for every option.

3. Run the application. Each model call produces a span on the **Traces** output of your OpenTelemetry Source.

### Trace Strands Agents

[Strands Agents](https://strandsagents.com/latest/) emits spans for each agent invocation, model call, and tool use. It reads the standard `OTEL_EXPORTER_OTLP_*` variables, so set the variables from [Configure the OpenTelemetry Exporter](#configure-the-opentelemetry-exporter), including `OTEL_SEMCONV_STABILITY_OPT_IN="gen_ai_latest_experimental"`. Without that variable, Strands emits an older version of the GenAI attributes.

```python theme={null}
from strands import Agent
from strands.telemetry.config import StrandsTelemetry
from strands_tools import calculator, current_time

telemetry = StrandsTelemetry()
telemetry.setup_otlp_exporter()

agent = Agent(tools=[calculator, current_time])

if __name__ == "__main__":
    result = agent("I was born in 1993. How old am I?")
    print(result)
```

This example uses the default Amazon Bedrock model provider, so it also needs AWS credentials with Bedrock access.

### Trace Cloudflare Agents

[Cloudflare Agents](https://developers.cloudflare.com/agents/) running on Workers emit GenAI spans through [Workers Observability](https://developers.cloudflare.com/agents/runtime/operations/observability/tracing/). Workers export telemetry to a destination that you create in the Cloudflare dashboard, so you set the Mezmo endpoint and ingestion key on that destination instead of in environment variables.

1. In the Cloudflare dashboard, go to **Observability > Destinations** and select **Add destination**.

2. Configure the destination:
   * **Destination name**: a name such as `mezmo-traces`.
   * **Destination type**: **Traces**.
   * **OTLP endpoint**: `https://logs.mezmo.com/otel/v1/traces`.
   * **Custom headers**: a header named `apikey` with your Mezmo ingestion key as its value.

3. Enable traces in your Worker's Wrangler configuration and list the destination by name:

   ```jsonc theme={null}
   {
     "observability": {
       "traces": {
         "enabled": true,
         "destinations": ["mezmo-traces"]
       }
     }
   }
   ```

4. If your agent calls the [Vercel AI SDK](https://ai-sdk.dev/) directly, wrap it with `wrapAISDK()` so that model calls produce spans with GenAI attributes. Cloudflare instruments its supported agent frameworks automatically.

   ```typescript theme={null}
   import * as ai from "ai";
   import { wrapAISDK } from "agents/observability/ai";

   // storeMessages and storeTools add prompts, responses, and tool payloads to spans. They're off by default.
   const { generateText } = wrapAISDK(ai, { storeMessages: true, storeTools: true });
   ```

5. Redeploy the Worker. Spans can take several minutes to reach your Pipeline. The destination's status in the Cloudflare dashboard shows whether delivery succeeded.

### Trace Claude SDK Calls

The Claude SDK emits OpenTelemetry spans for Messages API requests, tool runner tool uses, and Claude Managed Agents session requests. It sends them to the tracer provider that your application registers. Claude SDK tracing is in beta. See [Set up OpenTelemetry with the Claude SDK](https://platform.claude.com/docs/en/manage-claude/opentelemetry/sdk) for other languages and every option.

1. Install the Claude SDK, the OpenTelemetry SDK, and the OTLP HTTP exporter:

   ```bash theme={null}
   pip install --upgrade anthropic opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
   ```

2. Set the environment variables from [Configure the OpenTelemetry Exporter](#configure-the-opentelemetry-exporter).

3. Register a tracer provider before your first Claude call:

   ```python theme={null}
   from anthropic import Anthropic
   from opentelemetry import trace
   from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
   from opentelemetry.sdk.trace import TracerProvider
   from opentelemetry.sdk.trace.export import BatchSpanProcessor

   # The exporter reads the Mezmo endpoint and ingestion key from the OTEL_EXPORTER_OTLP_* variables.
   provider = TracerProvider()
   provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
   trace.set_tracer_provider(provider)

   # content_mode="content" adds prompts and responses to spans. The default, "metadata_only", leaves them out.
   client = Anthropic(
       open_telemetry={"tracer_provider": provider, "traces": {"content_mode": "content"}}
   )

   message = client.messages.create(
       model="claude-opus-5-5",
       max_tokens=1024,
       messages=[{"role": "user", "content": "Summarize today's error spikes."}],
   )
   print(message.content)

   # Send buffered spans before a short-lived script exits.
   provider.force_flush()
   ```

4. Run the application. Each request produces an `anthropic.messages.create` span on the **Traces** output of your OpenTelemetry Source.

> [!WARNING]
>
> With `content_mode="content"`, spans carry the full prompts and responses that your application exchanges with Claude. Mezmo then processes and stores that content under your Pipeline's Destinations and their retention settings. Use the default `metadata_only` mode when prompts can contain data that must not leave your application.

### Trace Claude API and Managed Agents Activity

The Claude API can also send spans for every request in your Claude Console organization, including model runs, server tool uses, and Claude Managed Agents turns, without code changes. You need the admin role in the Claude Console organization. Claude API tracing is in beta. See [Set up OpenTelemetry with the Claude API](https://platform.claude.com/docs/en/manage-claude/opentelemetry/api) for the full reference.

1. In the Claude Console, open the [OpenTelemetry settings page](https://platform.claude.com/settings/telemetry-destinations) and select **Add destination**.
2. In **Provider**, select **Custom**.
3. In **OTLP endpoint URL**, enter `https://logs.mezmo.com/otel`.
4. In **Request headers**, add a header named `apikey` with your Mezmo ingestion key as its value.
5. In **Content mode**, select **Metadata only** or **Full content**.
6. In **Workspaces**, select the workspaces whose spans Mezmo should receive.
7. Select **Add destination**.
8. In the destination's **More actions** menu, select **Test**.

The test sends an `anthropic.test` span to your OpenTelemetry Source. Spans from the Claude API carry the resource attribute `service.name` set to `claude-platform`, so they arrive with the `app` annotation `claude-platform`.

If you also trace the Claude SDK, each Messages API request has both an SDK span and an API span. To count tokens once per request, follow Anthropic's guidance on [counting tokens](https://platform.claude.com/docs/en/manage-claude/opentelemetry#counting-tokens).

### Send Langfuse Spans to Mezmo

The Langfuse Python SDK v3 and later and the JS SDK v4 and later are built on OpenTelemetry and attach their span processor to the global tracer provider. To send the same spans to Mezmo as well as to Langfuse, add a Mezmo exporter to that provider:

```python theme={null}
from langfuse import get_client
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
trace.set_tracer_provider(provider)

# Langfuse adds its own span processor to the global provider.
langfuse = get_client()
```

See [Using Langfuse with an existing OpenTelemetry setup](https://langfuse.com/faq/all/existing-otel-setup) for filtering and isolation options.

### Add GenAI Attributes to Your Own Spans

When your LLM provider or agent framework has no tracing of its own, create the spans yourself and set the GenAI attributes on them. This example records one chat call with the OpenTelemetry Python SDK:

```python theme={null}
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
trace.set_tracer_provider(provider)
tracer = trace.get_tracer("support-agent")

with tracer.start_as_current_span("chat gpt-4o", kind=trace.SpanKind.CLIENT) as span:
    span.set_attribute("gen_ai.operation.name", "chat")
    span.set_attribute("gen_ai.provider.name", "openai")
    span.set_attribute("gen_ai.request.model", "gpt-4o")

    # Call your model here, then record what it returned.
    span.set_attribute("gen_ai.response.model", "gpt-4o-2024-08-06")
    span.set_attribute("gen_ai.usage.input_tokens", 412)
    span.set_attribute("gen_ai.usage.output_tokens", 96)

provider.force_flush()
```

For the full list of attributes and span names, see the [GenAI semantic conventions for spans](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/).

## Work with GenAI Spans in Your Pipeline

The OpenTelemetry Source passes each span through in its OpenTelemetry JSON form, with GenAI attributes under `message.record.attributes`. A model call span looks like this on the **Traces** output:

```json theme={null}
{
  "message": {
    "type": "trace",
    "resource": {
      "attributes": {
        "service.name": "support-agent",
        "host.name": "agent-host-01"
      }
    },
    "scope": {
      "name": "support-agent",
      "attributes": {}
    },
    "record": {
      "name": "chat gpt-4o",
      "kind": 3,
      "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
      "spanId": "00f067aa0ba902b7",
      "startTimeUnixNano": "1792443306688749000",
      "endTimeUnixNano": "1792443308102331000",
      "status": { "code": 0 },
      "attributes": {
        "gen_ai.operation.name": "chat",
        "gen_ai.provider.name": "openai",
        "gen_ai.request.model": "gpt-4o",
        "gen_ai.response.model": "gpt-4o-2024-08-06",
        "gen_ai.usage.input_tokens": "412",
        "gen_ai.usage.output_tokens": "96"
      }
    }
  },
  "annotations": {
    "app": "support-agent",
    "host": "agent-host-01"
  },
  "timestamp": "2026-10-19T20:55:06.688Z"
}
```

Processor data fields start at the event's `message`, and attribute names that contain dots go in quotation marks. See [Syntax for Editing Pipeline Component Configuration Values](https://docs.mezmo.com/telemetry-pipelines/syntax-for-editing-pipeline-component-configuration-values.md) for details. Use these data fields when you configure Processors:

| To work with | Data field |
| - | - |
| The application or agent | `.resource.attributes."service.name"` |
| The kind of operation, such as `chat`, `invoke_agent`, or `execute_tool` | `.record.attributes."gen_ai.operation.name"` |
| The model that served the request | `.record.attributes."gen_ai.response.model"` |
| Token usage | `.record.attributes."gen_ai.usage.input_tokens"` and `.record.attributes."gen_ai.usage.output_tokens"` |
| A whole conversation or agent session | `.record.attributes."gen_ai.conversation.id"` |
| Failed operations | `.record.status.code`, where `2` means error |

These data fields follow the OpenTelemetry GenAI convention. Spans from OpenInference and Langfuse carry the same information under other names:

| To work with | OpenInference data field | Langfuse data field |
| - | - | - |
| The model | `.record.attributes."llm.model_name"` | `.record.attributes."langfuse.observation.model.name"` |
| Token usage | `.record.attributes."llm.token_count.prompt"` and `.record.attributes."llm.token_count.completion"` | `.record.attributes."langfuse.observation.usage_details"`, a JSON object |
| The kind of operation | `.record.attributes."openinference.span.kind"`, such as `LLM`, `TOOL`, or `AGENT` | `.record.attributes."langfuse.observation.type"`, such as `generation` or `tool` |
| A whole session | `.record.attributes."session.id"` | `.record.attributes."langfuse.session.id"` |

Attribute values often arrive as strings, so convert token counts to numbers before you add or compare them. For the complete event shape, see [OpenTelemetry Event Shapes](https://docs.mezmo.com/telemetry-pipelines/opentelemetry-event-shapes.md#traces).

To keep GenAI spans apart from your other traces, add a [Route](https://docs.mezmo.com/telemetry-pipelines/route-processor.md) Processor that matches on `gen_ai.operation.name`, or on `openinference.span.kind` for OpenInference spans. To reduce trace volume, use the experimental [Trace Sampling](https://docs.mezmo.com/telemetry-pipelines/trace-sampling-processor.md) Processor, which can sample traces with errors at a different rate from other traces. To forward the spans to another tool that accepts OTLP, connect them to an [OpenTelemetry](https://docs.mezmo.com/telemetry-pipelines/open-telemetry-destination.md) Destination.

## Troubleshoot Trace Delivery

| Problem | What to check |
| - | - |
| The exporter, the Claude Console, or the Cloudflare destination reports HTTP status `401` or `403` | The `apikey` header is missing or holds the wrong value. Set it to a valid Mezmo ingestion key. |
| The exporter fails to send spans over gRPC, for example on port `4317` | The endpoint accepts only OTLP over HTTP. Set `OTEL_EXPORTER_OTLP_PROTOCOL` to `http/protobuf`, or use an OTLP HTTP exporter. |
| The endpoint returns HTTP status `413` | A single span is larger than the maximum message size. This happens most often with full prompt and response content. Turn off prompt and response capture, or lower the content limit, for example with the Claude SDK's `traces.max_content_bytes` option or the Claude Console's **Maximum content size in MB** setting. |
| No spans arrive from a short-lived script | The script exits before the batch processor sends its spans. Call `provider.force_flush()` before the script ends. |
| Spans arrive without `gen_ai.` attributes | Check which [convention](#choose-an-instrumentation-library) your library uses. OpenInference and Langfuse spans use other attribute names. For libraries that emit GenAI attributes, set `OTEL_SEMCONV_STABILITY_OPT_IN` to `gen_ai_latest_experimental`. |
