Reporting telemetry from an external agent
Use this guide to send telemetry from an agent that runs outside Runtype. Runtype reads traces from Flue, the Vercel AI SDK, LangGraph, the OpenAI Agents SDK, Mastra, and your own agent loop, and reads Cloudflare Agents SDK traces with the limits described in Cloudflare Agents SDK. If your agent emits OpenTelemetry data, configure the exporter without a Runtype SDK.
Runtype stores the imported data as external executions. Your agent keeps using its existing model provider and credentials. Runtype does not run the agent or bill the imported run as a Runtype execution.
Create a telemetry API key
Create a dedicated API key for telemetry. On the dashboard API Keys page,
select Telemetry Ingest. The permission group grants one scope:
TELEMETRY:WRITE.
Use a dedicated key because the key can appear in collector configuration, container environments, and other shared locations. Ingest only appends data, so the key does not need read or management permissions.
The API does not let one API key create another API key. Create the telemetry key in the dashboard, then store it in the exporter configuration.
Existing keys with AGENTS:WRITE continue to work with the ingest endpoints.
Configure the OTLP/HTTP exporter
Set one base endpoint and identify the Runtype agent that receives the runs:
Replace rt_YOUR_API_KEY with the telemetry API key. Replace
agent_YOUR_AGENT_ID with the ID of the Runtype agent that receives the runs.
OpenTelemetry appends /v1/traces, /v1/metrics, and /v1/logs to the base
endpoint for each signal. A single endpoint therefore configures all three
signals.
Create the agent in the dashboard before you send telemetry. Runtype displays runs per agent.
Runtype accepts OTLP over HTTP, not OTLP over gRPC. If your SDK defaults to gRPC, set the HTTP protocol:
Restart the agent. The next exported run appears in the Runs view.
Configure the exporter in code
If your application builds the OpenTelemetry pipeline in code, configure the trace exporter with the same endpoint.
For a Node.js application, use the following TypeScript configuration:
Replace agent_YOUR_AGENT_ID with the ID of the Runtype agent that receives
the runs.
For a Python application, configure the HTTP trace exporter as follows:
Replace agent_YOUR_AGENT_ID with the ID of the Runtype agent that receives
the runs.
Use this cURL request to verify the endpoint, API key, and content type with an empty OTLP/JSON export:
Set RUNTYPE_API_KEY to the telemetry API key before you run the command. The
endpoint returns HTTP 200 with {} when it accepts the empty export.
For Python, install opentelemetry-exporter-otlp-proto-http. The Python gRPC
exporter is the default, so select the HTTP exporter or set
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf.
Send traces from a supported framework
Each framework below needs an OpenTelemetry provider and exporter standing behind its instrumentation. Instrumentation alone emits spans that nothing sends, and the run never appears.
The Connect an existing agent page in the dashboard renders a paste-ready snippet for each of these, already pointed at the API host your account uses.
Mastra’s exporter is the one exception to the environment variables above: it
reads neither OTEL_EXPORTER_OTLP_ENDPOINT nor OTEL_EXPORTER_OTLP_HEADERS,
and it wants the full trace path, so pass the endpoint and headers to
OtelExporter yourself:
Flue reports an iteration count, a stop reason and approval decisions, so its runs reach the highest fidelity Runtype records. The other four have no approval or skill concept to report, so their runs reach content fidelity: the run, its model, usage, cost, tool calls, transcript and final output.
Cloudflare Agents SDK
Runtype reads traces from the Cloudflare Agents SDK (agents 0.22) but does not
offer it on the Connect an existing agent page, because of how Workers
traces a chat session. Three facts to plan around, verified against a real
AIChatAgent under wrangler dev:
-
Nothing self-instruments. A plain
AIChatAgentcallingstreamTextproduces no GenAI spans. Wrap the AI SDK once, at module scope, and call the wrapped namespace fromonChatMessage:Instrument the AI SDKContent is off by default here, unlike the other supported frameworks. Without
storeMessagesandstoreToolsthe run has no transcript, and with it no eval capture. -
The export is a dashboard destination, not an environment variable. The spans go through Workers Observability, so the endpoint and headers live on a destination you create in the Cloudflare dashboard, and
wrangler.jsonconly names it. Follow the Flue on Cloudflare Workers steps; theOTEL_EXPORTER_*variables do nothing on a Worker. OTLP export needs a Workers Paid plan, and every span of the request, storage calls included, is billed, so lowerhead_sampling_rateonce the first trace has landed. -
One WebSocket connection is one trace. Every chat turn sent over the connection
useAgentChatholds open nests under the request that upgraded it, so a session of five turns arrives as one trace carrying fiveinvoke_agentspans. Runtype files one run per trace, so that session is recorded as one run: the first turn’s transcript, the last turn’s answer, and every tool call in between. A turn served by a plain HTTP request is its own trace and records correctly. Treat an Agents SDK run as one connection.
When the trace does arrive, Runtype reads its canonical gen_ai.* attributes
as-is and lifts the first model call’s gen_ai.input.messages onto the run,
since the SDK records messages on chat spans only. Tool-approval legs
(tool_approval spans, which carry the real call’s id) are kept in Logs and
never counted as a second tool call.
Send several services through one collector
If an OpenTelemetry Collector exports several instrumented services in one request, identify each service with a resource attribute:
Replace agent_YOUR_AGENT_ID with the ID of the Runtype agent for that service.
For each trace, Runtype resolves the agent from the resource attribute first,
then the x-runtype-agent-id header, and then the runtype.agent.id attribute
on the invoke_agent span. The header provides the fallback for traces that do
not set the resource attribute.
Run several agents in one process
If one process runs several agents, set runtype.agent.id on the
invoke_agent span that opens each run:
Replace agent_YOUR_AGENT_ID with the ID of the agent for that run.
The resource attribute and header describe a process or export. The span attribute identifies a run when one process uses a shared resource for several agents.
If two invoke_agent spans claim different agents and neither the resource
attribute nor the header identifies the agent, Runtype rejects the trace as
ambiguous_agent_attribution. One trace represents one run. If agents
delegate inside one trace, set attribution at the resource or header level, or
export each invocation as a separate trace.
Runtype resolves attribution per trace. If some traces name an agent that your
key cannot use, Runtype stores the accepted traces and reports the rejected
traces as OTLP partial_success. If no trace is accepted, the endpoint returns
an error. Accepted traces do not return an error, so the exporter does not
resend them because another trace in the export was rejected.
Identify the instrumentation that produced a trace
Set runtype.adapter.name and runtype.adapter.version to name the library
that emitted a trace and the version it ran. Runtype reads them from the
resource first and from the run’s invoke_agent span second, and records the
pair on the run so you can tell which producer version a run came from.
@runtypelabs/flue-otel sets both for you: it stamps them on every
invoke_agent span it opens, so a Flue run identifies its producer even when
you do not wire the resource attributes. Your own resource attribute still wins
where you set both.
The pair is provenance. Runtype never uses it to gate, price, or attribute a run, so a value you choose changes nothing but what you can read back.
Understand imported telemetry
Runtype projects traces into external executions and eval capture. It retains
spans and span events in Logs. It also retains attributed OTLP LogRecord data
and links it to an execution when it includes a trace ID.
Runtype acknowledges metrics but does not store them. This lets a stock exporter use the same base endpoint without retrying a missing signal endpoint.
OTLP delivery can repeat an export. Runtype stores received rows as append-only
data and deduplicates retry records when it displays Logs and trace trees. Span
events remain separate. Repeated byte-identical LogRecord entries converge
to one displayed record.
Runtype requires agent attribution to store OTLP LogRecord data. Without
runtype.agent.id on the Logs resource or the x-runtype-agent-id header,
Runtype returns OTLP partial_success and does not store those records. Add
one of these attribution methods to make the records visible.
After attribution, Runtype stores LogRecord bodies and attributes under the
target agent’s logging policy. Set that policy to Off before enabling the
Logs signal if you do not want to retain application logs. Enable personally
identifiable information (PII) redaction when the payloads can contain personal
data.
Runtype derives the run, tool calls, token counts, and a cost estimate from the traces. It calculates the display-only cost with the Runtype model catalog. Your provider charges you for the model calls, so treat the cost as an estimate.
To capture conversation content, set the generative AI (GenAI) content attributes on your spans. Runtype maps them to the run as follows:
Some instrumentations use different attribute names. Runtype also recognizes
flue.tool.call.arguments and flue.tool.call.result from
@flue/opentelemetry, which preserve tool results that are arrays or strings,
and the Vercel AI SDK’s ai.prompt.messages, ai.response.text,
ai.toolCall.args, and ai.toolCall.result. If a span carries both names,
Runtype uses the standard attribute. Runtype applies the same scrubbing and
size limits to an aliased value, and excludes every recognized spelling from the
retained span attributes in Logs.
OpenInference instrumentations spread a message list across indexed attributes
such as llm.input_messages.0.message.content. Runtype reassembles those into
the standard attributes before it reads them, and excludes the whole family from
Logs the same way.
If you use Flue, the first-party @runtypelabs/flue-otel instrumentation
reports an exact loop iteration count, a stop reason, and rolled-up usage.
See Instrumenting a Flue agent.
From 0.5 it also sends the transcript, system prompt and tool arguments and
results under the standard attribute names by default, so its runs carry a
transcript and can be captured as eval cases; each kind can be switched off
through its content option.
Point exactly one Flue instrumentation package at a Runtype endpoint. If you
export with both @runtypelabs/flue-otel and @flue/opentelemetry, Runtype
receives two invoke_agent spans for one run. The reported token counts and
cost then double.
Content capture is opt-in at the producer: Runtype stores conversation content
only when your instrumentation puts it on the span. That opt-in is not always the
gen_ai.* attributes. A supported framework’s own vocabulary counts too, and
those instrumentations record content by DEFAULT, so a run from the Vercel AI
SDK, LangGraph, the OpenAI Agents SDK or Mastra carries a transcript unless you
turn recording off upstream (recordInputs: false / recordOutputs: false on
the AI SDK, OPENINFERENCE_HIDE_INPUTS=true for OpenInference). An agent’s
logging policy governs the Logs tier, not the run’s transcript.
Runtype also keeps content out of the raw span attributes it retains in Logs. It strips every recognized content name and every vendor content family, so no payload reaches the raw Logs dump, which applies neither the caps nor the scrubbing.
The families a normalizer reads reach the run through the projection instead:
the gen_ai.* content attributes, the Vercel AI SDK prompt, response and tool
call keys, and OpenInference’s llm.input_messages.*, llm.output_messages.*
and the input.value / output.value pair on tool spans. The remaining
families are stripped from Logs and read by no normalizer, so Runtype stores
their payloads nowhere: OpenInference’s retrieval.documents.*,
llm.prompt_template.* and embedding.embeddings.*, Mastra’s
mastra.<span type>.input / .output, and the Cloudflare Agents SDK’s
cloudflare.agents.metadata.* and cloudflare.agents.runtime_context.*.
Before you enable content capture, account for these limits:
- Scrubbing and size limits. Runtype redacts credential-shaped strings before storage. It drops a content attribute over 256 KiB, content that exceeds the 384 KiB per-span budget, or content beyond the 2 MiB export budget. Runtype drops each affected value whole instead of truncating it.
- Partial-trace fidelity. Runtype records the fidelity that each run achieves. This distinguishes an empty model response from content that the exporter omits.
Use compatibility ingest without OpenTelemetry
To send unified execution events without OpenTelemetry, post a batch to the compatibility endpoint:
Replace rt_YOUR_API_KEY with the telemetry API key. Replace
agent_YOUR_AGENT_ID with the ID of the target Runtype agent. Replace
YOUR_EXECUTION_ID with a unique ID for the run.
The request body contains a batch from the unified execution event vocabulary.
The endpoint accepts the same TELEMETRY:WRITE key and populates the agent’s
Runs view and eval capture, including the transcript when the events carry
it. For the event schema, see the API Reference.
This endpoint does not retain the structural span and span-event records that
Logs and trace_execution use. On these runs, trace_execution reports
json_ingest_compatibility_path as the availability reason. Use the OTLP/HTTP
endpoints when you need Logs, parent-child trace trees, or OTLP LogRecord
data. Use compatibility ingest when Runs and eval capture meet your needs.
Next steps
Continue with one of these guides:
- Instrumenting a Flue agent: use the first-party Flue instrumentation.
- Authentication: review API key scopes.
- Self-hosting (BYOC): run an exported flow or agent on your own infrastructure.