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, OpenClaw, Hermes, Pi, the Cloudflare Agents SDK, and your own agent loop. If your agent emits OpenTelemetry data, configure the exporter without a Runtype SDK.
Runtype shows each imported run on the receiving agent’s Runs view. Your agent keeps using its existing model provider and credentials. Runtype does not run the agent or bill the imported run as a Runtype run.
Before you begin
You need the following:
- A Runtype agent that receives the runs. Runtype files every run under an agent, so it rejects a trace that names no agent.
- An API key with the
TELEMETRY:WRITEscope. Keys withAGENTS:WRITEalso work. See Create a telemetry API key. - If you send OTLP, configure a trace exporter. The OpenClaw, Hermes, and Pi plugins include one. Other frameworks need an OpenTelemetry SDK with an OTLP/HTTP trace exporter. See Configure the OTLP/HTTP exporter.
The Connect an agent page in the dashboard can set up the first two for you. To use it, follow these steps:
- Open the Connect an agent page. Inside a product, click Connect an agent in the product’s dock or on an empty product’s architecture, or click New > Connect an agent. The agent joins the product as a capability when its first run arrives. Outside a product, the Agents page links to the page in a workspace with no agents, and the command palette lists it as Connect an agent.
- Select your framework.
- Under the framework chips, select the agent that receives the runs, or create one from the same list. In a workspace with no agents, the page creates the agent for you.
- Under What you want, select Only see its runs. To also let Runtype call the agent over A2A, keep Let Runtype call it, which includes its runs. OpenClaw, Hermes, and Pi support only Only see its runs. For the call setup, see Connect an agent from the dashboard.
- Under Who sets it up, select Your coding agent or Yourself.
With Your coding agent, copy the setup prompt and paste it into a coding agent, such as Claude Code, Cursor, or Codex, in your project. The prompt carries the install command, environment variables, and framework-specific code or CLI commands. It contains no key. Instead, it tells the coding agent to request a test key that can only send telemetry, with a command similar to the following:
You approve the request in the browser. The coding agent then uses the printed
key wherever the prompt shows the rt_YOUR_TELEMETRY_KEY placeholder.
runtype keys is an alias of runtype api-keys.
With Yourself, follow the numbered steps. Flue, the AI SDK, LangGraph, and the OpenAI Agents SDK have four: Install, Add your key, Instrument, and Import it first. Mastra has the first three. For Cloudflare Agents, Add your key configures a Workers Observability destination, and the last step is Name the destination. A step is marked done after you copy its block. If no run arrives, expand Not seeing runs? for the common causes.
Create a telemetry API key
In a workspace with no agents, Connect an agent creates a
TELEMETRY:WRITE test key for you. A test key stays revealable, so a later
visit to the page fills the blocks again. Store the key as RUNTYPE_API_KEY
for OpenClaw, Pi, and the exporter-based recipes. Hermes reads the same key from
RUNTYPE_OTEL_API_KEY.
In a workspace that already has agents, select the receiving agent, select Yourself, and then click Create a test key in the Add your key step. The page shows Test key created with the masked key and fills the key into the environment block.
To create a production key, click Create a live key. A production key is
shown once, in a dialog, and you can’t reveal it afterwards. The page doesn’t
fill it into the blocks, so paste it wherever rt_YOUR_TELEMETRY_KEY appears.
You can also create the key on the dashboard API Keys page. Select the
Telemetry Ingest permission group, which grants the TELEMETRY:WRITE
scope.
We recommend a dedicated key, because the key can appear in collector
configuration, container environments, and other shared locations. Ingest only
appends data, so the key needs no read or management permissions. An API key
can’t create another API key. Create the telemetry key in the dashboard, or
request one with runtype keys request and approve the request in the browser.
Configure the OTLP/HTTP exporter
For a framework that uses an OpenTelemetry SDK, set one base endpoint and identify the Runtype agent that receives the runs:
Replace the following:
rt_YOUR_API_KEY: the telemetry API key.agent_YOUR_AGENT_ID: the ID of the Runtype agent that receives the runs.
If you used Connect an agent with a test key, its .env block already contains both values.
OpenTelemetry appends /v1/traces, /v1/metrics, and /v1/logs to the base
endpoint for each signal, so one endpoint configures all three signals.
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 agent’s 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, install opentelemetry-exporter-otlp-proto-http and
configure the HTTP trace exporter as follows:
Replace agent_YOUR_AGENT_ID with the ID of the Runtype agent that receives
the runs. The Python gRPC exporter is the default, so select the HTTP exporter
or set OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf.
Verify that runs arrive
To check the endpoint, the API key, and the content type, send 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.
While Connect an agent waits for the first run, the dock shows Waiting
for the first trace from AGENT_NAME., where AGENT_NAME is the receiving
agent’s name. The progress panel beside the steps counts the completed steps,
for example 2 of 4 complete.
After the first run arrives, the page shows AGENT_NAME is connected. If you selected Let Runtype call it, the page shows it after you also connect the agent. Click Open run to open the run. Inside a product, click Go to the product canvas to return to the product. Under Next, if you want, the page offers Let Runtype call it when you chose only to see runs, and Switch to a live key. After the agent joins a product, the page also offers Add a web chat surface. Click Copy prompt to copy a prompt that asks your coding agent to add a web chat surface to the product.
If a run arrives but its spans don’t identify the framework that you selected, the Framework step shows A trace arrived, but nothing confirms it came from FRAMEWORK. Still watching. If the spans identify a different framework, it shows Received a FRAMEWORK run; still waiting for FRAMEWORK. instead, naming both frameworks. Check that the instrumentation or native plugin loads before the agent runs.
To confirm from the API, list the agent’s imported runs with a key that has
AGENTS:READ. A TELEMETRY:WRITE key can’t read runs.
Replace the following:
agent_YOUR_AGENT_ID: the ID of the receiving agent.rt_YOUR_READ_API_KEY: an API key withAGENTS:READ.
Send traces from a supported framework
Each framework in this table needs an OpenTelemetry provider and exporter behind its instrumentation. Instrumentation alone emits spans that nothing sends, and the run never appears. OpenClaw, Hermes, and Pi use native plugins instead.
When you select Yourself, Connect an agent shows this setup for each
framework as copyable blocks, pointed at the API host your account uses. The
instrumentation file is instrumentation.ts for Flue and the Vercel AI SDK, instrumentation.py for
LangGraph and the OpenAI Agents SDK, and src/mastra/index.ts for Mastra. The
Your entry point block imports that file before the agent runs. The Vercel
AI SDK also shows Your generateText call, because it turns telemetry on per
call. Mastra shows Your agent run instead of an entry point block.
OpenClaw, Hermes, and Pi use native plugins. Each has three setup blocks: Terminal, then an environment block, then a CLI block. The environment and CLI blocks are Gateway environment and OpenClaw CLI for OpenClaw, Hermes environment and Hermes CLI for Hermes, and Shell environment and Pi CLI for Pi.
The Cloudflare Agents chip sets up the Workers Observability path that the
Cloudflare Agents SDK section describes. Its blocks are Terminal,
Cloudflare destination, src/traced-ai.ts, Your onChatMessage, and
wrangler.jsonc. The Any OTel source chip has no
install block. It shows the environment block and the attributes that Runtype
reads.
Mastra’s exporter reads neither OTEL_EXPORTER_OTLP_ENDPOINT nor
OTEL_EXPORTER_OTLP_HEADERS, and it needs the full trace path. Pass the
endpoint and headers to OtelExporter:
OtelExporter loads the exporter package that its protocol names, and it
treats that package as optional, so npm i @mastra/otel-exporter does not
install it. Install @opentelemetry/exporter-trace-otlp-proto alongside it for
http/protobuf. Without it, the exporter logs one console error and sends
nothing.
An agent authored in Runtype and self-hosted with @runtypelabs/runtime needs
none of this. Set RUNTYPE_API_KEY on the deployment and the runtime exports
the same spans itself, with conversation content on by default. See
Self-hosting (BYOC).
The exporter-based frameworks report the model, token usage, cost, tool calls, transcript, and final output when their instrumentation sends those values. Flue also reports the iteration count, stop reason, and approval decisions. The OpenClaw and Hermes adapters, and the Pi coding agent extension, send content only when you enable capture.
Flush before the process exits
A batch span processor holds spans for about 5 seconds, and it exports child
spans before their parents. A process that exits inside that window loses the
closing invoke_agent span. Without that span, the run shows as still running.
A short-lived run, such as a CLI invocation or a serverless handler, hits this
every time.
Shut the pipeline down before the process ends. In an instrumentation.ts file
that owns the SDK or the provider, register the flush beside it:
For Mastra, call mastra.shutdown(), which flushes the batch processor. Put
it in a finally block, because a run that threw is the one whose closing span
matters most. In a serverless handler, call await provider.forceFlush()
before you return. The Python SDK’s TracerProvider registers its own exit
handler, so the LangGraph and OpenAI Agents SDK setups need no extra step.
OpenClaw adapter
The OpenClaw plugin uses Gateway hooks to export a trace for each observed
agent turn. It reports an invoke_agent span, an attempt-level chat span,
and execute_tool spans for observed completed tools. The chat span does
not describe each physical provider request. You don’t need a separate
OpenTelemetry provider.
Review the plugin and its requested capabilities before granting conversation
access. Install and enable @runtypelabs/openclaw-adapter:
The allowConversationAccess grant lets the plugin receive model and
conversation hooks.
Inspect the loaded plugin without --json: JSON output can expose environment
values.
Set these variables on the OpenClaw Gateway process:
Replace agent_YOUR_AGENT_ID with the receiving agent’s ID and
rt_YOUR_TELEMETRY_KEY with a TELEMETRY:WRITE key. The adapter posts to
https://api.runtype.com/v1/otel/v1/traces by default. For another Runtype
environment, set RUNTYPE_OTLP_URL to its full OTLP trace URL.
Restart the Gateway to load the plugin and inspect its runtime hooks:
Content capture is off by default. Set RUNTYPE_CAPTURE_CONTENT=true to send
bounded prompt, answer, and tool content for transcripts and eval case capture.
Omit that line from the example for a metadata-only trace. The plugin redacts
configured secret values, but that filter cannot identify every secret in an
agent’s content. If OpenClaw supplies no content for a turn, the run has no
transcript to capture as an eval case.
Send a normal OpenClaw message after restarting the Gateway, then check the
receiving agent’s Runs view. OpenClaw may emit llm_output after
agent_end. The adapter waits up to one second for pending model or tool
output before exporting the available facts. Telemetry failures don’t fail the
agent turn, and the plugin has no durable retry queue.
Hermes adapter
The Hermes plugin uses Hermes observer hooks. It sends one OTLP trace per
observed agent turn, with an invoke_agent span, model activity, and completed
tool calls. You don’t need a separate OpenTelemetry provider.
Install and enable the plugin from its public repository. It uses only the
Python standard library, so it needs no pip install.
Set these variables in the Hermes gateway or CLI process environment:
Replace agent_YOUR_AGENT_ID with the receiving agent’s ID and
rt_YOUR_TELEMETRY_KEY with a TELEMETRY:WRITE key. The adapter posts to
https://api.runtype.com/v1/otel/v1/traces by default. For another Runtype
environment, set RUNTYPE_OTEL_ENDPOINT to its full OTLP trace URL.
Content capture is off by default. Set RUNTYPE_OTEL_CAPTURE_CONTENT=true
to send bounded prompt, answer, and tool content for transcripts and eval case
capture. Omit that line from the example for a metadata-only trace. The plugin
redacts configured API keys and common key patterns, but that filter cannot
identify every secret in an agent’s content.
Restart a running gateway with hermes gateway restart, or run one CLI turn
with hermes chat --oneshot -q "Reply with one sentence". A successfully
exported turn appears in the receiving agent’s Runs view.
Hermes can return a provider error before it emits the terminal
on_session_end hook. The plugin cannot treat api_request_error as terminal
because Hermes may retry or switch providers. A long-running gateway therefore
doesn’t export that trace immediately. Turns that reach on_session_end
report an observed failure or interruption as an error.
Pi adapter
@runtypelabs/pi-otel reports Pi agent runs. It has one
binding for each Pi runtime:
@runtypelabs/pi-otel/coding-agentis an extension for the Pi coding agent. The Pi chip on Connect an agent sets it up.@runtypelabs/pi-otel/durableobserves a pi-durable harness, including one hosted by the Cloudflare Agents SDK’sPiHarness.
Both bindings send OTLP/JSON to Runtype directly, so you don’t need a separate
OpenTelemetry provider. Each Pi run becomes one Runtype run, with an
invoke_agent span, one chat span for each model response, and one
execute_tool span for each tool result. Runtype prices the run from the
model name. Upstream, pi-durable is experimental and PiHarness is in beta.
The package supports pi-durable and the Pi coding agent 1.0.x only.
Pi coding agent
Install the package with Pi’s package manager:
Set these variables in the shell that starts Pi:
Replace agent_YOUR_AGENT_ID with the receiving agent’s ID and
rt_YOUR_TELEMETRY_KEY with a TELEMETRY:WRITE key. The extension posts to
https://api.runtype.com/v1/otel/v1/traces by default. For another Runtype
environment, set RUNTYPE_OTLP_URL to its full OTLP trace URL.
Content capture is off by default, because a coding session carries source
code. Set RUNTYPE_CAPTURE_CONTENT=true to send prompts, answers, and tool
content for transcripts and eval case capture. Omit that line for a
metadata-only trace.
Start pi in the same shell and send a prompt. A run is one prompt until Pi
settles, so retries and compaction stay in the same run. The run appears in
the receiving agent’s Runs view when Pi finishes answering. Without a key
or an agent ID, the extension does nothing. Set RUNTYPE_DEBUG=true to print
the reason to stderr.
pi-durable and PiHarness
Install the package next to pi-durable:
Call observePiDurable right after you open the harness, and await
recover before any work resumes. With PiHarness, do both in the harness
factory, before you return the harness:
harnessId must be stable for the harness’s storage. pi-durable numbers
conversations per storage, so it keeps runs from different Durable Objects
apart. recover() picks up a run that was in flight when the Durable Object was
evicted, so it continues in the same Runtype run. Await it before any work
resumes. Do not attach through this.harness.pi(). It resolves only after
PiHarness has resumed Pi’s work, so the observer misses work that resumes
after an eviction. Each input submission becomes one run. A submission that ends unanswered is a
failed run.
The observer sends each model and tool span as Pi commits it, and the
invoke_agent span when the submission settles. In a Worker, keep the request
open for delivery with ctx.waitUntil(this.telemetry.flush()) after each
prompt. In Node, await observer.flush() before the process exits.
The durable binding sends content by default. Pass content: false for a
metadata-only trace, which also turns off eval capture. To drop one kind of
content, set that kind to false, for example
content: { systemInstructions: false }. A redact function can rewrite or
drop each value before it is sent. For every option, see the
package README.
Report each Pi run through one producer only. Two producers for the same run
send two invoke_agent spans, and Runtype counts its tokens and cost twice.
Cloudflare Agents SDK adapter
The Cloudflare Agents SDK adapter, @runtypelabs/cloudflare-agents, is a beta
integration published to npm. The Cloudflare Agents chip on
Connect an agent sets up the
native Workers Observability path, not this adapter.
Use the adapter when you need durable delivery of each user request from your
own Worker.
The adapter is validated against agents@0.22.0,
@cloudflare/ai-chat@0.11.0, and ai@6.0.277.
The adapter records each user request through a companion Durable Object and sends OTLP/HTTP to Runtype directly. It does not need a Cloudflare trace exporter. If you use Cloudflare tracing for Workers diagnostics, keep it on. Those traces still appear in Logs, but they don’t complete an adapter run.
The adapter needs a second Durable Object class beside your AIChatAgent, with
its own SQLite migration and these Worker-only bindings:
RUNTYPE_AGENT_ID: the Runtype agent that owns these imported runs.RUNTYPE_API_KEY: aTELEMETRY:WRITEkey stored withwrangler secret put.RUNTYPE_BASE_URL:https://api.runtype.com.
Keep all three bindings server-side. Don’t send the API key to the browser, place it in a WebSocket frame, or put it in a client bundle. Export the companion class from the Worker module and add its binding and SQLite migration alongside your Agent class:
Use a stable application submission ID for one user request, a separate conversation ID for attribution, and the Agents SDK request ID as the exchange ID. Generate a new submission ID for an explicit rerun. Keep the same one for transport retries and reconnects. Don’t derive any of these IDs from a Cloudflare trace, a message’s position in a conversation, or the latest message body.
The following sample wraps a streamText call in an AIChatAgent:
readStableSubmissionId, readConversationId, readStableExchangeId,
myModel, toModelMessages, and myServerTools belong to your application.
Validate the IDs before you call the adapter. Keep your existing
streamText(...).toUIMessageStreamResponse() call, because the wrapper does not
consume or replace the response stream. Keep your existing abortSignal,
onFinish, onError, and prepareStep callbacks in the options you pass. The
adapter runs its callbacks alongside them.
The adapter records no content by default. Each content field turns on one
kind:
messages: user and assistant content.instructions: system and developer messages, including the instruction in effect afterprepareStep.tools: tool arguments and results. Tool names and durations are recorded either way.errors: error text.
If you set messages and leave instructions unset, the adapter records
instructions too. To record messages without instructions, set instructions
to false.
To remove or replace a value before the adapter stores or sends it, pass a
redact(kind, value) function in content. Return undefined to omit the
value. After redaction, the adapter drops any value larger than 32 KiB. To
lower that limit, set maxBytes in content.
The adapter retries a delivery that was not acknowledged, and it completes a run only after it observes the final result. A run with a missing final result or a delivery gap stays incomplete instead of showing as successful. A failure to report telemetry does not fail the response to your user.
The adapter does not support approval continuations, client-executed tools, automatic subagent trees, linking to Cloudflare traces, or approving and resuming from Runtype. Cloudflare traces in Logs can show incomplete infrastructure activity, but they don’t supply output, usage, or a final result for an adapter run.
Cloudflare Agents SDK
This path sends Cloudflare Agents SDK spans through Workers Observability. It
uses published packages and is separate from the
adapter. A plain AIChatAgent that calls
streamText creates no GenAI spans. Wrap the AI SDK once at module scope and
call the wrapped namespace from onChatMessage:
Content is off unless you set storeMessages and storeTools. Without them,
the run has no transcript and you can’t capture it as an eval case.
The export is a Workers Observability destination that you configure in the
Cloudflare dashboard. Set its endpoint and headers there, and name the
destination in wrangler.jsonc. The OTEL_EXPORTER_* variables do not
configure this path. Follow the
Flue on Cloudflare Workers
destination setup. It needs a Workers Paid plan, and Cloudflare bills every
sampled span.
Cloudflare can group several turns into one trace, or split one request across several traces. Runtype creates one run per trace, so a request paused for approval can appear as more than one run. Don’t count connections, traces, or runs as a count of user requests.
Slow and cancelled turns can emit cloudflare.warning.type = span_not_ended
without GenAI attributes. Runtype shows that activity in Logs as incomplete,
with an unknown outcome, and creates no run for it. An answer that the client
received does not fill in the missing output or usage.
Runtype reads the gen_ai.* attributes on these traces and uses the first chat
span’s gen_ai.input.messages as the run’s transcript. tool_approval spans
stay in Logs and don’t become a second tool call. Runtype shows the
approval requests and decisions that it finds in a trace as read-only history.
It does not approve, resume, or link approvals across traces. With logging off,
the decisions remain but their content and reasons don’t. Runs imported before
approval history was recorded have no approval history, which does not mean
that no approval happened.
Instrument a custom agent loop
If you instrument your own loop, emit spans that follow the OpenTelemetry generative AI (GenAI) semantic conventions. Runtype creates a run for a trace that contains a GenAI operation or model telemetry. A trace without either stays in Logs as a diagnostic and creates no run.
The following table lists the span attributes that Runtype reads:
To mark a run as failed, set the invoke_agent span’s status to ERROR. A span
with status UNSET and a recorded exception event also counts as failed. A
status of OK counts as successful.
The following sample opens an invoke_agent span with a child chat span and
a child execute_tool span:
conversationId, messages, callModel, and lookupOrder belong to your
application. Use an opaque value for gen_ai.conversation.id, not personal
data or credentials. To add conversation content to the run, see
Capture conversation content.
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 is the fallback for traces that don’t
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.
Leave the x-runtype-agent-id header and the runtype.agent.id resource
attribute unset in that process. Either one takes precedence over the span
attribute, so every run goes to the agent that it names.
If two invoke_agent spans in one trace name different agents, and neither the
resource attribute nor the header names the agent, Runtype rejects the trace as
ambiguous_agent_attribution. Runtype also rejects a trace whose spans carry
different runtype.agent.id resource attributes. If agents delegate inside one
trace, set the agent at the resource or header level, or export each invocation
as a separate trace.
Identify the instrumentation that produced a trace
To record which library emitted a trace and its version, set
runtype.adapter.name and runtype.adapter.version. Runtype reads them from
the resource first and from the run’s invoke_agent span second, and records
the pair on the run.
@runtypelabs/flue-otel sets both on every invoke_agent span that it opens,
so a Flue run identifies its producer without resource attributes. Your own
resource attribute takes precedence when you set both. Runtype never uses the
pair to accept, price, or attribute a run.
Record the agent version and alias
To file a run under the agent version that produced it, set
runtype.agent.version_id. Set runtype.agent.alias to the release alias
(live, staging, and so on) that the version was resolved through. Runtype
then groups the run with that version in Runs, trends, and eval capture, the
same way it groups hosted runs.
- Runtype reads the attributes from the resource first and from the run’s
invoke_agentspan second. - The version must belong to the agent that the run is attributed to. Runtype drops a version ID that it does not find on that agent and still accepts the run.
- A run that names an alias records that it was resolved through an alias. A run with only a version ID records that it was pinned to that version.
runtype.agent.alias_revisionis provenance only. Runtype never groups or filters on it.
An agent exported with @runtypelabs/runtime emits these attributes
automatically from its ExportedAgent.release. You do not need to set them in
your host code.
Capture conversation content
Runtype stores conversation content only when your instrumentation puts it on
the span. The Vercel AI SDK, LangGraph, the OpenAI Agents SDK, and Mastra
record content by default, so their runs carry a transcript unless you turn
recording off. To turn it off, set recordInputs: false and
recordOutputs: false on the AI SDK, or OPENINFERENCE_HIDE_INPUTS=true for
OpenInference. The agent’s logging policy applies to Logs, not to the run’s
transcript.
The OpenClaw, Hermes, and Pi adapters use the standard gen_ai.* attributes.
For OpenClaw, Hermes, and the Pi coding agent, content capture is off by
default. Set RUNTYPE_CAPTURE_CONTENT=true for OpenClaw and the Pi coding
agent, or RUNTYPE_OTEL_CAPTURE_CONTENT=true for Hermes. The Pi durable
binding sends content unless you pass content: false. If an adapter
receives no content, the run has no transcript to capture as an eval case.
For your own loop, set the GenAI content attributes. Runtype maps them to the run as follows:
Runtype also reads these alternative attribute names:
flue.tool.call.argumentsandflue.tool.call.resultfrom@flue/opentelemetry.- The Vercel AI SDK’s
ai.prompt.messages,ai.response.text,ai.toolCall.args, andai.toolCall.result. - OpenInference’s indexed attributes, such as
llm.input_messages.0.message.content, and theinput.valueandoutput.valuepair on tool spans.
If a span carries both a standard name and an alternative name, Runtype uses the standard attribute.
From version 0.5, @runtypelabs/flue-otel sends the transcript, system prompt,
and tool arguments and results by default, so its runs can be captured as eval
cases. Each kind can be turned off with its content option. See
Instrumenting a Flue agent.
Point exactly one Flue instrumentation package at Runtype. If you export with
both @runtypelabs/flue-otel and @flue/opentelemetry, Runtype receives two
invoke_agent spans for one run, and the token counts and cost double.
Runtype applies these limits to content:
- It redacts credential-shaped strings before storage.
- It drops a content attribute over 256 KiB, and content past 384 KiB on one span.
- It drops content past 2 MiB for the whole trace. Splitting a trace across exporter batches does not reset this limit.
Runtype drops each affected value whole instead of truncating it.
Runtype calculates a cost estimate from the token counts and its model catalog. Your provider charges you for the model calls, so treat the cost as an estimate.
Logging privacy
Runtype removes content attributes from the span attributes that it shows in
Logs, and stores that content on the run. When the agent’s logging is on,
Logs also shows each model call’s scrubbed input, instructions, and output
in separate fields. Runtype discards the following content attributes and stores them
nowhere: OpenInference’s retrieval.documents.*, llm.prompt_template.*, and
embedding.embeddings.*, Mastra’s mastra.<span type>.input and .output,
and the Cloudflare Agents SDK’s cloudflare.agents.metadata.* and
cloudflare.agents.runtime_context.*.
Runtype also stores OTLP LogRecord bodies and attributes under the receiving
agent’s logging policy. To keep log bodies and attributes out of storage, set
the agent’s Verbose logging option to Off in its Logging settings
before you enable the logs signal. Runtype then keeps each record’s severity and
trace ID, but not its body or attributes, and the log detail view shows
Verbose logging off. If the payloads can contain personal data, turn on
personally identifiable information (PII) redaction.
If secret scrubbing or your PII policy changes a conversation ID, or the ID is longer than 256 UTF-8 bytes, Logs shows a stable digest of it instead. Distinct IDs keep distinct digests. The digest is a pseudonym, not anonymization.
To store LogRecord data, name the agent with runtype.agent.id on the logs
resource or with the x-runtype-agent-id header. Without either, Runtype
returns OTLP partial_success and discards those records. A trace ID on a
LogRecord links it to the trace in Logs, but it does not create a run.
Runtype acknowledges metrics but does not store them. To keep telemetry, send traces or logs.
Limits
The following table lists the limits that apply to one OTLP export and to one trace:
Runtype accepts gzip, deflate, or no Content-Encoding. It rejects zstd
with 415. It accepts application/x-protobuf and application/json bodies.
Runtype accepts or rejects each trace in an export whole. It never stores part of a trace’s spans from one request.
Troubleshoot ingest errors
The following table lists the errors that the OTLP endpoints return:
Runtype checks each trace in an export separately. If some traces are accepted
and others are rejected, the endpoint returns 200 with an OTLP
partial_success that names the rejected traces. Your exporter does not resend
the accepted traces. The error status in the table applies only when Runtype
accepts no trace from the export. If the rejected traces have different causes,
the status is 400.
OTLP exporters retry 429, 502, 503, and 504, but not 500. Rejected
exports still count against your ingest capacity. Runtype removes duplicates
that retries create when it shows Logs and trace trees.
A 200 response means Runtype accepted the export. The data can take a short
time to appear in Logs. Deleting a run, or tightening its agent’s logging
or PII policy, removes content that Runtype hasn’t delivered yet.
Inspect ingest health
If exported data never appears, check ingest health with a separate key that
has ANALYTICS:READ or AGENTS:READ. A TELEMETRY:WRITE key can’t read this
endpoint.
Replace rt_YOUR_READ_API_KEY with an API key that has ANALYTICS:READ or
AGENTS:READ.
The response counts the records that Runtype accepted, is still delivering, and
has confirmed over the last 30 days, and it reports the ingest capacity that
remains. To see the counts for one trace, add ?traceId=YOUR_HEX_TRACE_ID. The
response never contains transcripts or payloads. For every field, see the
API Reference.
The following table describes the status values:
The status summarizes the delivery receipts that Runtype recorded. It doesn’t test whether Logs can return the data at that moment.
Use compatibility ingest without OpenTelemetry
To send execution events without OpenTelemetry, post a batch to the compatibility endpoint:
Replace the following:
rt_YOUR_API_KEY: the telemetry API key.agent_YOUR_AGENT_ID: the ID of the target Runtype agent.YOUR_EXECUTION_ID: a unique ID for the run. It can’t start withotel-.
The following table describes the request body:
To send more than 500 events, send several batches with the same executionId.
A later batch updates the run. For example, it can complete a run or a tool
call that an earlier batch opened. For the event schema, see the
API Reference.
The endpoint returns 202 with a body similar to the following:
The endpoint uses the same key scopes and returns the same 400, 403, 404,
409, and 413 errors as the OTLP endpoints. An executionId that starts with
otel- returns 409.
Runs from this endpoint appear in the agent’s Runs view and eval capture,
with a transcript when you send one. They also appear in Logs with an agent
span and one span per tool call. Use OTLP/HTTP when your producer already emits
OpenTelemetry, or when you need a span per model call or OTLP LogRecord data.
Use compatibility ingest when a JSON batch is easier to produce than a trace.
Next steps
Continue with one of these guides:
- Bring your own agent: compare the ways to connect an external agent.
- Improving agents from production: turn imported runs into eval cases.
- Working with logs: inspect the spans and log records that your agent sends.
- 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.
- Working with runs: find runs and read the run detail page.