Skip to main content
Galtea exposes a public OpenTelemetry (OTel) endpoint. OTel is the open standard for emitting traces. The endpoint is an OTLP collector, where OTLP is the OTel export protocol. Send your traces there to enable two use cases:
  • Create production sessions from real traffic. Uncorrelated spans become production Sessions that Monitors evaluate automatically. Spans that name a test case become a non-production session instead, which no Monitor scores. See Monitor Real User Traffic via OpenTelemetry.
  • Enrich existing Galtea Traces. Spans correlated to a Trace become Span records on it. Correlation uses the shared trace ID from the traceparent header Galtea propagates during Direct Inference, or the explicit galtea.trace.id span attribute. Galtea also sends that attribute’s value in the baggage header of each conversation turn call, so your agent can copy it onto its spans. See W3C Trace Context Propagation.
The endpoint, authentication, and exporter configuration on this page are the same for both.

Endpoint and authentication

Configure your OTel exporter to send traces to the Galtea collector. Every request must carry an Authorization: Bearer <Galtea-API-key> header — without it, the gateway returns 401 Unauthorized before the request reaches the collector. All examples below use the Galtea platform hostname: otel.platform.prod-main.galtea.ai.
If your organization has a private Galtea deployment, replace the hostname with your tenant-specific endpoint:
Your Galtea account team will provide the exact hostname during onboarding.

Configure your exporter

The OpenTelemetry SDK reads endpoint and headers from environment variables. Use shell substitution to expand ${GALTEA_API_KEY} before the value is stored in OTEL_EXPORTER_OTLP_TRACES_HEADERS:
The same ${GALTEA_API_KEY} syntax works in a docker-compose.yml environment: block, but only when GALTEA_API_KEY is set in the shell that runs Compose or in a .env file Compose loads. Docker Compose substitutes ${...} from those sources when it renders the config. It does not read another entry in the same environment: block. If the variable is unset there, the exporter receives the literal string ${GALTEA_API_KEY} and the gateway returns 401 Unauthorized.
Use OTEL_EXPORTER_OTLP_TRACES_ENDPOINT (the per-signal variant), not OTEL_EXPORTER_OTLP_ENDPOINT. The base variable auto-appends /v1/traces to the URL, but the Galtea collector expects the /otel/traces path.
The OTel exporter does not interpolate placeholders inside OTEL_EXPORTER_OTLP_TRACES_HEADERS — it sends the value verbatim. Exporting Authorization=Bearer $GALTEA_API_KEY without shell expansion (for example, single-quoted, or written into a .env file that your runtime loads without expanding it) literally sends the string $GALTEA_API_KEY and the gateway returns 401 Unauthorized. Ensure the expansion happens at export time, not later.

Verify your key and endpoint

A quick way to confirm the header is being applied correctly is to POST an empty payload and inspect the status code:

How span content maps to Span records

When a span becomes a Span record — whether it enriches an existing trace or is assembled into a production conversation — its input and output are filled per field, using the first source that provides a value:
  1. galtea.span.* explicit attributes — full control, always wins.
  2. gen_ai.* GenAI semantic conventions — the OpenTelemetry standard for LLM telemetry.
  3. Recognized managed-platform span shapes — platform-specific formats Galtea knows.
  4. A configured span-source rule — otelInputSourceRule or otelOutputSourceRule on the Version, the Product, or the organization, which reads the field out of a named non-LLM span. See Recover the turn input from a non-LLM span.
A turn’s input and output follow a different order, because a turn has many spans: a configured source rule decides the field, else the turn’s single root span, else the earliest input and the latest output. A configured rule that fires on no span of the turn leaves that field empty; it never falls back. See Monitor real user traffic. If you set no attributes, spans are still ingested. A span type Galtea does not recognize is also ingested: Galtea normalizes the value to a known span type and keeps your original label in the span’s metadata, under the key galtea.span.original_type. The span type follows its own order, because only galtea.span.type is a Galtea attribute:
  1. galtea.span.type — always wins.
  2. The span kind your platform already declares — MLflow’s mlflow.spanType, or a span shape that names the kind by itself.
  3. gen_ai.operation.name — the OpenTelemetry GenAI operation, such as chat or embeddings.
  4. SPAN — when nothing above names a kind.
So a span imported from an instrumented platform is typed without any Galtea attribute: an MLflow RETRIEVER span becomes a RETRIEVER span, which is what Galtea reads retrieval context from. A vendor word Galtea spells differently still maps, so MLflow’s LLM becomes GENERATION.
These keys had different names before version 5.0.0, and Galtea still reads the old ones. See Dataset, Trace, and Span Renames if your collector already sends them.
These OTel fields are mapped automatically for every span, whichever source above you use:
  • span.name → name
  • startTimeUnixNano / endTimeUnixNano → startTime / endTime + latencyMs (computed)
  • span.status → error (fallback when galtea.span.error is not set; only for STATUS_CODE_ERROR)
  • parentSpanId → parentTraceId (parent-child hierarchy)
All remaining unmapped span attributes are collected into the metadata field, so no data is lost.

Token usage and cost

Galtea reads the token counts your spans report and adds them up per turn. Send them with the OpenTelemetry GenAI keys: A turn’s inputTokens, outputTokens and tokens are the sum over every span of that turn, so a turn that makes three LLM calls reports the three calls added together. A span that reports no usage adds nothing. Send no usage at all and the turn keeps what Galtea already had: nothing when no Model is linked to the Version, or the estimate Galtea derived from the turn text when one is. Galtea also recognizes the usage a managed platform reports in its own format. MLflow Tracing’s mlflow.chat.tokenUsage works with no Galtea attribute. When a span carries both, the gen_ai.usage.* keys win, field by field. Cost is derived from those counts and the per-token price of the Model linked to the Version. Link a Model with a known price and the turn’s cost is filled. With no Model, or with no price for it, cost stays empty rather than zero. A reported count replaces one Galtea estimated earlier: your own figure is the accurate one. A cost you reported yourself is kept, and the new counts do not replace it: Galtea recomputes only a cost it derived from the counts and the Model price, never a different figure you set. Spans of a turn may arrive in several export batches, and each batch raises the turn’s total instead of replacing it. Re-sending the same spans changes nothing.
Retrieval context (RAG): to record what your system retrieved, type a span as RETRIEVER and put the retrieved data in its output (for example via galtea.span.output). This is how Galtea reads retrieval context: it is derived from your RETRIEVER spans, and the output has no required shape.
Some common instrumentation styles do not populate input/output yet: Traceloop / OpenLLMetry (gen_ai.prompt.0.content-style flattened attributes) and OpenInference (input.value). Their data is still stored in the span’s metadata field, so nothing is lost, but evaluations that read the span input and output will not see it.

Attributes that route a conversation

The keys above fill a Span record. The keys below decide which session Galtea assembles the spans into. They apply to uncorrelated spans only, so a span already linked to a Trace ignores them. Name the version in one of three ways. galtea.version.id points at a Version you already created. galtea.product.id plus the galtea.version.fact.<key> attributes describe the agent state instead. The third way sends no galtea.version.fact.* attributes. It sends the standard attributes that your organization or product lists as version attributes, and Galtea builds the facts from them. Either of the last two ways can drop galtea.product.id and send the product key attributes instead. In the last two ways Galtea finds or creates the matching Version with the rules of Get or Create Version: the same facts reuse one Version, and one changed value creates a new one. The route follows the first present of galtea.version.id, galtea.version.fact.*, and the version attributes, so a resource that sets more than one is read by the first. Its later attributes are ignored. When a resource has galtea.product.id and the key attributes, galtea.product.id wins. Monitor Real User Traffic via OpenTelemetry shows each setup. Galtea rejects the uncorrelated spans of a resource, while the rest of the payload still ingests, when it sends fact attributes, or attributes your organization lists as version attributes, with no usable galtea.product.id and its resource attributes route to no single product of your organization (no product has those key values, or two products with keys of the same size do), when the facts are not valid, whether sent as fact attributes or built from the version attributes (a value that is not text or an integer, more than 32 keys, or more than 2000 bytes in all), or when the product does not exist in your organization. The last case also covers an API key whose role cannot create a Version when no Version holds the facts yet. Galtea rejects one conversation, while the rest of the payload still ingests, when the test case does not exist, when it belongs to another product than the version, when two spans of one conversation name different test case IDs or two resources do, or when the conversation key already holds another session on that version. Galtea also rejects a span whose trace ID matches a Direct Inference turn that was removed. It does not assemble that span into a new session. Those spans are counted in the export response’s partialSuccess.rejectedSpans. The response’s partialSuccess.errorMessage names the reason that applied, so your collector log says what to change. Monitor Real User Traffic via OpenTelemetry walks through the full setup.

Learn More

Monitor Real User Traffic via OpenTelemetry

Turn your live traffic’s traces into production sessions that Monitors evaluate.

W3C Trace Context Propagation

Attach OTel spans to the test runs Galtea triggers via Direct Inference.