Skip to content

Architecture and migrationGuide

Join Anthropic's OpenTelemetry export with gateway traces

Anthropic now exports traces for Messages API requests and Claude Managed Agents sessions to your observability platform. If your gateway already emits spans, the same call can appear three times. This guide sets one owner per signal and joins the layers by request id.

By INFRO Editorial

Published

8 min read

One application request entering a central AI gateway and branching to many model routes

In short

Treat the gateway span as the record of what your application asked for and what it was estimated to cost, and the Anthropic export as the provider's view of the same call. Join them on a request id, and let only one source feed cost.

Key takeaways

  • Anthropic launched OpenTelemetry export in beta on 14 October 2026, covering Messages API requests and Claude Managed Agents sessions, so treat attribute names as unstable until you have checked them.
  • A single call can produce up to three spans: your SDK or app span, the gateway span, and the Anthropic-side span. Token counts and cost appear in more than one of them.
  • Pick one source for cost and tag every span with its origin, so dashboards never add tokens or dollars across layers.
  • Join on a request id, not on timestamps; expect one gateway request to map to zero, one or several provider calls once caching, failover and retries are involved.
  • Span delivery is best effort, so spans can show you where to look, but a reconciliation against the provider's cost report is what proves the totals.

What Anthropic shipped, and what is still unconfirmed

On 14 October 2026 Anthropic's release notes announced OpenTelemetry export in beta, so you can trace Messages API requests and Claude Managed Agents sessions in your own observability platform. The Claude SDK emits spans in Python, TypeScript, C#, Go, Java, PHP and Ruby. To get spans from the API itself, you add an OpenTelemetry destination in the Claude Console.1

That is the whole confirmed surface for this article. I did not read the span reference, so the rest of this guide does not state which attributes the spans carry, whether they include a request id, how they are sampled or how fast they arrive. Those are things to check in a short test before you build dashboards on them, and the sections below say exactly what to check. The join method works whatever the answers are, as long as one field on the Anthropic side identifies the request.

The beta label matters for a second reason. OpenTelemetry's own GenAI conventions have moved to a separate repository, and the pages inside the main semantic-conventions site are marked as no longer maintained.2 If your dashboards rely on attribute names, pin the version of the conventions you built against and expect to revisit them.

Three layers can describe one call

If your application calls Claude through a gateway, one request can leave up to three kinds of span:

LayerEmitted byWhat it knowsWhere it goes
ClientYour app or the Claude SDKThe call as the application made it, including time spent waitingYour collector
GatewayThe gatewayRequest id, model, route, timing, token counts, estimated cost and configured metadata3Your collector
ProviderAnthropic, through a Console destinationThe request as Anthropic processed itThe destination you configured
The three vantage points on one Claude call.

Whether the SDK's spans still work when its base URL points at a gateway is something to test rather than assume. The same goes for whether the Console destination is scoped to an organization or narrower. Both change how many layers you really have.

The risk is not missing data. It is the same call showing up in three places with token counts on at least two of them, and a dashboard that adds them.

Which signals duplicate, which add

Duplicated across gateway and provider spans, in all likelihood: model, outcome, token counts and total latency. INFRO's span carries request id, model, route, timing, token counts and estimated cost, and it deliberately leaves out prompt and response captures.3 Anything the Anthropic export also carries for those fields is a duplicate, and you should pick one.

What the provider export can add, to be verified against real spans:

  • Time inside Anthropic versus time outside it. The gateway sees total latency to the provider. A provider-side span, if it records its own duration, lets you subtract it and see network and queueing on your side.
  • Managed Agents sessions. The release note says sessions are traced.1 A gateway sees only the model calls that pass through it, so work that happens inside a session on Anthropic's side is visible only here, if at all.
  • Calls that skipped the gateway. If the destination covers a whole account, it will include traffic that never touched your gateway. That is useful for finding bypass, which is a separate problem covered in detecting provider usage that bypassed your gateway.

What only the gateway span can add: the route that was chosen, response-cache status, guardrail actions and anything decided before a provider call. INFRO's spans carry infro.cache.status on chat completions with the values hit, miss, bypass, uncacheable or disabled, and infro.guardrail.action when guardrails acted.3 A cache hit has a gateway span and no provider span at all, and that is correct, because the provider never billed it.

Rule one: a single owner for cost

Cost is where double counting does damage, because tokens turn into dollars and the dollars feed budgets. Decide up front:

  1. Tokens and cost come from one layer. For a gateway deployment that is normally the gateway span, because it is the only layer that knows which negotiated rate applies and which route served the call. INFRO's request log is explicit that its figure is an estimate and operational evidence, not an invoice.4
  2. The provider export is evidence, not a second ledger. Use it for timing, for sampling a handful of calls against the gateway's counts, and for the traffic the gateway never saw.
  3. Tag every span with its origin at the collector, so a query can exclude a layer by construction.
  4. Check totals against the provider's own cost report, not against a sum of spans. Span delivery is lossy by design, as the next section shows.

If you also run Claude Code, note that it has its own OpenTelemetry metrics and events that report usage and estimated cost.5 That is a fourth possible cost source. Give it the same treatment: a separate origin tag and no summing.

Rule two: join by request id, not by time

Timestamps do not join spans reliably: clocks differ, one call can be retried, and concurrent requests have near-identical start times. Use ids.

Step 1: put the gateway request id on your own span

The gateway returns or records an id for every request; INFRO's request records have ids such as req_8fJq3kT2 and are readable at GET /v1/requests/{id}.4 Capture it in your application span as an attribute, so the client layer already carries the gateway key.

Step 2: find the provider's identifier

Look at a few real spans from the Anthropic destination and find the field that identifies the request on Anthropic's side. Then check whether the gateway also stores that provider-side id. If it does not, you have a gap: the gateway id and the provider id live in different layers with nothing linking them. Resolve that before building anything. Options include storing the provider's response id on the client span, or relying on any trace context that the export honors. Test the second with a call carrying a known trace id, and do not assume it.

Step 3: build the join table at the collector

Tag origin at ingestion so every later query can separate the layers. This OpenTelemetry Collector fragment is a sketch; attribute names are yours to choose.

collector.yaml (illustrative)
processors:
  transform/gateway:
    trace_statements:
      - context: span
        statements:
          - set(attributes["telemetry.origin"], "gateway")
  transform/provider:
    trace_statements:
      - context: span
        statements:
          - set(attributes["telemetry.origin"], "anthropic")

Route each receiver through its own processor in separate pipelines, then export both to the same backend. The tag is the only thing that keeps the layers apart afterwards.

Step 4: query one layer for cost, the other for timing

sql
-- illustrative schema: one row per span
SELECT g.gateway_request_id,
       g.cost_estimate_usd,                 -- cost: gateway only
       g.duration_ms - p.duration_ms AS outside_provider_ms
FROM spans g
LEFT JOIN spans p
  ON p.provider_request_id = g.provider_request_id
 AND p.origin = 'anthropic'
WHERE g.origin = 'gateway';

The LEFT JOIN is deliberate. Gateway rows with no provider match are not errors; they are the cache hits, the requests refused before a call, and the calls sent to other providers.

Failure modes to design for

Cardinality is not one to one. A gateway request maps to zero provider calls on a cache hit or a refusal, to one in the normal case, and to more than one when a failed attempt is retried elsewhere. INFRO's failover retries a request that times out or returns a 5xx before the response starts, without the client seeing it. A failed attempt may leave a provider span and little or no token usage. If you want attempt-level detail, INFRO's request log records the provider connection and fallback attempts.4 Aggregate provider spans per gateway id before comparing.

Delivery is best effort. INFRO sends spans outside the response path, retries once on a server or network error, does not retry a 4xx from your collector, and drops spans on persistent failure rather than block requests. Its docs say not to use the channel as the sole accounting record.3 Assume the Anthropic beta has similar limits until you read its documentation, and alert on the ratio of joined to unjoined rows.

Payload content. The gateway's spans exclude prompt and response captures.3 Check whether the provider export includes content, and decide in your prompt logging policy before you turn it on in production, since a destination you forgot about is a second copy of the data.

Attribute drift. It is a beta and the conventions it follows are moving.12 Put a test in CI that fails when an attribute you depend on disappears from sample spans.

Getting the gateway layer without extra instrumentation

If you run INFRO, its OpenTelemetry export pushes spans from the request path to your own collector, with the endpoint and headers set in the console under Settings → Gateway → Trace export, as OTLP/HTTP protobuf, and it is included on every plan.3 That is the gateway layer described above. For totals, INFRO's daily reconciliation compares estimated spend against the cost reported by Anthropic's own cost API, which is the check that spans alone cannot give you. Details are in the exports and OpenTelemetry docs (opens in a new tab).

Checklist before you rely on the join

  • Send 20 test calls with known ids and confirm each one appears in the gateway layer and in the Anthropic destination.
  • Identify the provider-side request identifier field and confirm it is stable across the two layers.
  • Add a telemetry.origin tag at the collector for every source.
  • Choose the cost source and exclude the others from every cost panel.
  • Confirm cache hits and refused requests appear as unmatched gateway rows, not as errors.
  • Alert on the share of gateway rows with no provider match once cache and failover traffic is excluded.
  • Reconcile monthly totals against the provider's cost report.
  • Review what the provider export carries about prompt content before enabling it broadly.

For the generic setup of GenAI spans and privacy, see OpenTelemetry for LLM calls, and for multi-call agent runs, tracing an agent run.

Questions readers ask

Do I still need gateway spans if Anthropic exports its own?
Yes, if you route more than one provider or use failover. The Anthropic export describes calls that reached Anthropic. It cannot show a request that was answered from a gateway cache, refused by a budget before any provider call, or sent to another provider after a failure.
Can I sum token counts from both exports to get a total?
No. Both layers describe the same call, so adding them counts it twice. Choose one source for tokens and cost, and use the other only for timing and for checking the first.
Is the Anthropic export safe to rely on in production?
Anthropic released it as a beta, so attribute names and behavior can change. Pin your collector transformations, alert on missing attributes, and keep your reconciliation against the provider's cost report as the check on totals.

From the team that builds it

Where this shows up in INFRO

INFRO is a control plane in front of the provider accounts you already hold. The capabilities below are the parts of it this article touches, each with the documentation that defines its behaviour.

  • OpenTelemetry export

    Available with a plan

    Spans pushed to your own collector from the request path, with the endpoint and headers set in the console. Included on every plan.

    Read the documentation for OpenTelemetry export
  • Request traces and payload logs

    Available with a plan

    Per-request timing detail, with prompt and completion logging off by default and a retention window you choose.

    Read the documentation for Request traces and payload logs
  • Usage and cost per request

    Available with a plan

    Every request records its model, latency, token or unit counts, and estimated provider cost, queryable by project and key.

    Read the documentation for Usage and cost per request
  • Provider cost reconciliation

    Available with a plan

    Daily comparison of estimated spend against the cost reported by OpenAI's and Anthropic's own cost APIs, using a separate admin credential, with the likely cause of a persistent gap and a suggested rate you can apply. Included on every plan.

    Read the documentation for Provider cost reconciliation
  • Response cache

    Available with a plan

    Opt-in, exact-match cache for chat completions. An identical request inside the time-to-live you set is answered from the cache and never reaches your provider, so your provider does not bill it. Every plan; the longest time-to-live depends on the plan. No semantic matching.

    Read the documentation for Response cache
  • Automatic failover

    Available with a plan

    A route that times out or 5xxs before the response starts is retried elsewhere without your client seeing it.

    Read the documentation for Automatic failover
  • Negotiated provider rates

    Available with a plan

    Enter the rates your contracts actually carry, per model and optionally per provider, so estimated cost uses them before the vendor's list rate. Included on every plan.

    Read the documentation for Negotiated provider rates

INFRO accounts are self-serve. Provider usage is billed to you by your providers; INFRO charges a software subscription and takes 0% of model spend. See plans.

Sources

Numbered in order of first citation. Each was opened and checked against the claim on the date shown.

  1. 1.Claude Platform release notes (opens in a new tab) — Anthropic. Accessed Oct 29, 2026.
  2. 2.Generative AI semantic conventions (moved) (opens in a new tab) — OpenTelemetry. Accessed Oct 29, 2026.
  3. 3.Exports & OpenTelemetry (opens in a new tab) — INFRO. Accessed Oct 29, 2026.
  4. 4.Request traces (opens in a new tab) — INFRO. Accessed Oct 29, 2026.
  5. 5.Manage costs and demonstrate ROI for Claude Code on Google Cloud (opens in a new tab) — Anthropic. Accessed Oct 29, 2026.

About this article

Read on 29 October 2026: Anthropic's Claude Platform release notes (the 14 October entry announcing the beta), INFRO's exports and request-trace docs, OpenTelemetry's GenAI conventions page and Anthropic's Claude Code cost article. I did not read Anthropic's span reference, so span names, attributes, sampling, delivery behavior and cost fields are left out as unconfirmed and written as checks to run.

Last checked against its sources on . The next review is due by . No separate named reviewer is recorded for this article.

How articles are written, sourced and corrected is set out in the editorial standards.