Skip to main content
Tessary reads these fields off every span you send. Anything not listed here is still carried verbatim into the attribute bag and is queryable, but it drives no ingest-time processing. The contract is standard first. The platform reads OTel (OpenTelemetry) and gen_ai semantic-convention names wherever one exists, accepts third-party dialects for compatibility, and defines exactly one attribute of its own: tessary.call_site.id.
For the practical subset, meaning what you have to emit to get a working project, read Span requirements. This page is the exhaustive lookup behind it.

Where spans arrive

Both transports write down the same path, so a stored span is identical no matter how it arrived. See API keys for the credential and its errors, and Configuration for the transport variables.

How to read the tables

  • Field is the wire name. There is one spelling per field. Tessary reads exactly the names listed here, with no compatibility aliases and no deprecated-spelling fallbacks.
  • Applies to says which spans the field is read for. always means every span; otherwise it names the gen_ai.operation.name family or the carrier the field is only read on.
  • Origin says where the name comes from: OTLP proto for a protobuf span field rather than an attribute, OTel semconv for the general semantic conventions, gen_ai semconv for the GenAI conventions, OpenInference and Traceloop for third-party dialects accepted for compatibility, Tessary for the one overlay attribute, carrier extension for the generic input and output carriers, and any for the attribute bag.

Identity and timing

There is no attribute for a turn. The turn is the trace: one turn per producer trace id, with no separate node.

Status and errors

error.type has one precedence: the producer’s own error.type wins, and only when it is absent does status.message supply the label, whitespace-collapsed and capped at 120 characters. State neither and the tool-call column keeps a placeholder, because a failure with no label is still a failure, while the span column stays null. error_type is a label for grouping failures. The full prose stays recoverable from the stored payload.

Session, thread, and end user

Kind

gen_ai.operation.name is the single kind source. It is an open enum: retrieval, rerank, guardrail, plan, reasoning, and handoff are Tessary’s accepted extension values, and any name containing memory normalizes to memory. Three more attributes refine or stand in for the kind.

Handoffs are structural

A handoff between agents needs no attribute. It is already wire-encoded as an execute_tool span for the transfer call emitted by the first agent, followed by an invoke_agent span for the second under the same parent, which is the shape agent-framework instrumentation already emits. Ingest marks both spans truthfully, and the relationship is derivable from the span graph at read time. A producer that wants to state it explicitly may emit gen_ai.operation.name = handoff.

Tool calls

Model

Both spellings are kept because they answer different questions. provided_model_name is what the producer said; model_id is the resolved catalog key the price book is stated in.

Content

Message content is never truncated at ingest. It is bounded by count, not by size.
Payloads pass through PII redaction on the way in. The rules in force, including the built-in defaults, are on Settings → PII redaction.

Token usage

Tessary keeps the raw usage object in the span payload as an audit record and never reads it for arithmetic. When the producer reports no cost of its own, these buckets are priced on arrival from the price book with cost_source='inferred', and never repriced.

Cost

Per-bucket figures win over any total. A lone total lands in input_cost, because the span’s total cost is generated from the four buckets and splitting a total across them would be a derivation rather than a reading.

Retrieved documents

Call site

This is the only sanctioned Tessary overlay attribute, and the dotted spelling roots a tessary.call_site.* namespace. Any producer can set it as a plain attribute, or a Collector attributes or transform rule can add it. No SDK is required. Without it, a span still lands and is still queryable, but it belongs to no call site, and the connect gate on a new project does not open on it. An untagged span carries a null call_site_id, so a filter on any call site excludes it outright.

Everything else

Accepted but not consumed

These arrive without being rejected and survive verbatim in the attribute bag. They drive nothing. Do not build against them.

Attributes with no reader

The consumed tessary.* surface is exactly one attribute. Every other tessary.* spelling has no reader anywhere on the platform, and its content belongs on the standard field instead. Ad-hoc alias spellings of canonical fields are never read either. sessionId, userId, user_id, enduser.id, and deployment.environment all fall in this category.

Limits

How long a stored trace survives afterward is a separate setting: see Data retention.

Invariants

Four rules govern how this contract changes.
  1. Standard first. A new field uses the OTel or gen_ai name when one exists. tessary.* is only for genuine gaps.
  2. Ingest fails open. The absence of any non-structural field degrades a feature. It never drops a span.
  3. Content is verbatim. Message and tool content is never truncated at ingest. The one exception is the 8,000,000-character payload cap above, which drops the span rather than clipping it.
  4. No compatibility aliases. Only the canonical spelling of each field is read. Third-party dialects are normalized at the edge as documented product features; ad-hoc alias spellings are not.

Span requirements

The practical subset: what to emit to get a working project.

API keys

The write-scoped credential ingest requires, and the errors it returns.

MCP server

How an agent reads these fields back, dataset by dataset.

Configuration reference

The OTLP transport, ingest buffer, and retention variables.