AI Data Plane telemetry schema

This page lists every field the AI Data Plane sends to CloudZero, and everything it leaves behind. Your security reviewers can read the exact contract here instead of inferring it from a network capture.

For what the AI Data Plane does, see AI Data Plane. For how it is deployed and how it fails, see AI Data Plane architecture.

ℹ️

The AI Data Plane is in preview. Coverage and capabilities are expanding. To get started, reach out to your account manager.

Two streams, two destinations

Two independent streams reach the egress collector, and the collector keeps them separate all the way to CloudZero. It routes on the resource identity of each span, so your gateway's telemetry never lands in the AI Data Plane's ingest source.

StreamProduced byCarriesDestination
Data Plane spansThe AI Data Plane's context engineWhich work each conversation turn was doinghttps://telemetry.cloudzero.com/v2/telemetry/cz-data-plane
Usage spansYour AI gatewayTokens, models, and costhttps://telemetry.cloudzero.com/v2/telemetry/<sink>

The <sink> names your gateway, for example litellm or bifrost. It is a free-form source name, so a newly supported gateway needs no chart upgrade. Both destinations are required when the egress collector is enabled; omitting one fails configuration validation rather than sending gateway usage to the wrong source.

Both streams travel as OpenTelemetry traces over OTLP, and both leave your network over HTTPS authenticated with your CloudZero API key.

Data Plane spans

The AI Data Plane emits one span, cz.context, recording the outcome of classifying one conversation turn.

A cz.context span carries a trace ID, span ID, parent span ID, trace flags, and start and end times, and is emitted with span kind internal. It carries no events and no links.

One span covers a whole turn, and a turn is usually several round trips to your gateway. The span takes the trace ID of the turn's closing round trip, which is what determines how it joins to usage. See How attribution and usage join.

Attributes fall into two namespaces, split by who consumes them. Attributes under cz.context.*, along with the standard OpenTelemetry keys, carry business attribution that CloudZero maps into typed columns. Attributes under cz.data_plane.* are operational detail that stays available for troubleshooting and is not carried downstream.

ℹ️

The cz.context attribute set is being revised while the AI Data Plane is in preview. Gate consumers on cz.data_plane.schema.version, and confirm the current fields before building anything long-lived on them.

Resource attributes

AttributeValueSet by
service.namecloudzero-ai-data-planeThe context engine's exporter
cz.data_plane.idThe device ID of your registered deploymentThe egress collector
cz.data_plane.collector_idThe device ID of your attached gatewayThe egress collector, when the deployment reports an attached device
cz.data_plane.collector_typegatewayThe egress collector, alongside cz.data_plane.collector_id

service.name is the value the collector routes on, which is why a gateway's own spans never reach the AI Data Plane's ingest source.

The device identity is minted at runtime, so on a fresh install the identity Secret does not exist yet. Spans exported during that window ship without the device attributes, and the collector adds them from the moment the Secret appears.

Context span attributes

AttributeTypePresent
cz.data_plane.schema.versionIntegerAlways, currently 1
session.idStringAlways
cz.attribution.idStringAlways
cz.data_plane.classification.outcomeStringsucceeded or failed
cz.data_plane.classifier.generationStringAlways, including on a failed classification
cz.context.activityStringOn success
cz.data_plane.activity.candidatesString arrayOn success
cz.data_plane.activity.confidencesDouble arrayOn success
vcs.repository.nameStringOn success, when the repository was identified
vcs.ref.head.nameStringOn success, when the branch was identified
vcs.change.idStringOn success, when the pull request was identified
cz.context.work_item.idStringOn success, when a work item was identified

session.id groups the spans of one client session without carrying the session identifier itself. Its value is either the session or thread key your client supplied, already hashed at intake, or a hash scoped to your organization when the client supplied no stable grouping. cz.attribution.id is the turn key, identifying the conversation turn the span describes.

A classification that terminally failed carries the outcome and the generation, and no result fields. That distinguishes a turn that will never be classified from one whose context span has not arrived yet.

The repository, branch, and pull request ride their standard OpenTelemetry keys rather than a cz.context.* spelling, because the convention adopts them from semantic conventions. Each of those four fields is independently optional; a classification can name an activity without naming a repository.

Work-item attribution stays off unless your organization configures one or more work-item patterns.

Reading the candidate arrays. The two arrays have equal length and matching positions, and hold at most 16 entries. The first candidate is always the same value as cz.context.activity. Candidate names are unique and ordered by descending confidence. Confidence is the classifier model's own uncalibrated estimate, from zero through one inclusive. The classifier includes every plausible alternative for the primary activity and excludes incidental or secondary work.

cz.data_plane.classifier.generation identifies the classification behavior that produced the span, currently 1.0.0. It is raised whenever prompt, grounding rules, or shipped defaults change, so results from different generations separate cleanly in reporting. It does not track deployment configuration such as the model or reasoning budget.

Usage spans

Usage spans are your gateway's own OpenTelemetry spans. CloudZero does not create them, modify their shape, or fill in missing model, token, or cost values. Their schema is your gateway's schema.

What the AI Data Plane needs from a gateway

Nothing in the architecture or the telemetry is specific to one gateway. Any gateway works when it does two things:

  • Exports OpenTelemetry usage spans to the egress collector, following the OpenTelemetry GenAI semantic conventions, so token counts and model names land on keys CloudZero already reads.
  • Propagates W3C trace context, carrying the incoming traceparent through to the spans it emits. That shared trace ID is the entire join between attribution and cost, so a gateway that drops it produces usage CloudZero cannot attribute.

A gateway that does both is supported, whichever one it is.

The values that matter

The conventions cover the call and its token counts. These are the attributes the cost picture is built from:

AttributeCarries
gen_ai.request.model, gen_ai.response.modelThe model asked for, and the one that served the call
gen_ai.usage.input_tokens, gen_ai.usage.output_tokensToken counts for the call
gen_ai.usage.cache_read.input_tokens, gen_ai.usage.cache_creation.input_tokensCache token counts, where the provider reports them
gen_ai.operation.name, gen_ai.provider.nameThe operation performed and the provider that served it

Cost is the exception. The GenAI conventions standardize tokens, not money, so each gateway spells cost its own way or leaves it out and expects you to derive it from tokens. Check your gateway's documentation for which it does.

For the full field list, read the OpenTelemetry documentation for the gateway you run.

Configuring the stream

Two things are worth confirming during setup, whichever gateway you use:

  • Keep message content off the stream. Gateways commonly offer to log prompts and responses alongside their usage telemetry, and some do it by default. Turn that off, so only metadata about each request is exported.
  • Point the exporter at the collector, not at CloudZero. Your gateway exports to the egress collector's OTLP endpoint inside your cluster. The collector is what holds the CloudZero credential and forwards to the gateway ingest source.

Two more things to expect when you read this stream back. OTLP export retries can redeliver a whole batch, so deduplicate on trace ID and span ID before counting or summing. And OTLP values are typed per attribute rather than per key, so the same attribute can arrive as an integer from one code path and a string from another; read both encodings rather than one.

Whatever identity your gateway attaches to its spans, such as the calling user or key, travels with them. The AI Data Plane neither adds nor removes those attributes, and your gateway's configuration decides what they hold.

Without this stream you still see which work happened. You do not see what it cost.

How attribution and usage join

Reading AI spend by business context takes both streams, joined on the trace ID.

The AI Data Plane forwards its trace context to your gateway using the W3C trace context standard. When your gateway honors that context, its usage span carries the same trace ID as the turn's cz.context span. A query for what a given piece of work cost runs in three moves:

  1. Select a succeeded cz.context span carrying the repository, branch, pull request, or work item you care about.
  2. Find the gateway usage spans sharing its trace ID.
  3. Sum the gateway's cost attributes.

A trace-ID join resolves to the turn's closing round trip, not the whole turn. A cz.context span carries one trace ID, the closing round trip's, while a turn is often several calls to your gateway. Read a trace-ID join as the cost of the closing call, and size that gap before using it for chargeback.

The join does not complete at all when your gateway ignores the forwarded trace context, does not export usage telemetry, suppresses an unsampled span, or omits cost attributes. Confirm your gateway carries trace context through to its own telemetry before you rely on joined numbers.

What never leaves your network

Data Plane spans are prompt-free by construction. The exporter builds each span from the fixed attribute list above and has no path for anything else, so the boundary is a property of the code rather than a configuration setting you could turn off.

The following never appear in a Data Plane span:

  • Prompts, responses, reasoning, summaries, tool input, or tool output
  • Classifier request or response bodies
  • Credentials, arbitrary URLs, or arbitrary error strings
  • Raw tenant, session, conversation, turn, capture, or provider identifiers
  • Values copied from untrusted headers or resource attributes
  • Model names, token counts, and cost, which arrive in your gateway's usage spans instead
  • Arbitrary provider metadata

String attributes take reviewed, finite values. Counts, durations, and confidences are numeric. A successful classification can include bounded artifact identifiers such as a repository or branch name, grounded in what the source actually supplied.

Two more boundaries are worth naming. Your organization's identity travels on the authenticated export channel rather than in span attributes, so the span contract carries no tenant field. And WebSocket traffic emits no spans at all, because it never reaches the context engine.

Your gateway's usage spans are governed by your gateway's configuration, not by this contract. Keeping message content out of that stream is a setting on your gateway, covered under Usage spans.

Schema versioning

cz.data_plane.schema.version is an integer, currently 1. Gate downstream consumers on that value rather than on whether a field is present, so a future contract change is a version bump you can detect instead of a field that quietly appears.

Once both streams land in CloudZero, AI costs sit alongside your cloud and SaaS spend. Allocate them with the same Dimensions and rules you already use, measure cost per unit, and track them in Dashboards.

ℹ️

Have questions or feedback? Reach out to your account manager.


Did this page help you?