> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.runtype.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.runtype.com/_mcp/server.

# Bring your own agent

> Use Runtype observability, evals, and chat surfaces with an agent that runs on your own infrastructure

Your agent can keep running where it runs today, whether you built it with
[Flue](https://flueframework.com), the Cloudflare Agents SDK, the Vercel AI SDK,
LangGraph, the OpenAI Agents SDK, Mastra, OpenClaw, Hermes, Pi, or your own loop. Runtype adds the
Runs view, Logs, trace trees, cost estimates, eval capture, the Persona chat
widget, surfaces, and the trace-to-eval improvement loop.

This page maps the three ways to connect an agent. You can use more than one of
them for the same agent.

## Choose how to connect your agent

The following table lists the three options, what you do for each, and what each
one gives you:

| Option                                | What you do                                                                                                                         | What it unlocks                                                                                                                                                                                          |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Send telemetry to Runtype**         | Export OpenTelemetry traces to `https://api.runtype.com/v1/otel`                                                                    | The Runs view, Logs, trace trees, token usage, and a display-only cost estimate. When the spans carry conversation content, you also get the run's transcript and eval case capture.                     |
| **Let Runtype call your agent**       | Register an `external` agent whose endpoint streams Runtype events (`runtype-stream`) or speaks A2A                                 | Testing from the dashboard, MCP, or SDK, Persona chat widgets, surfaces and products, and schedules. Runtype records tool calls and cost for each run. An A2A endpoint can also be an eval suite target. |
| **Run a Runtype agent on your cloud** | Export an agent authored in Runtype and host it with `@runtypelabs/runtime` ([Self-hosting](/developer-guides/guides/self-hosting)) | The agent reports each run to the `/v1/otel` endpoint from your infrastructure: the run, its trace tree, transcript, tool calls, usage, and cost estimate. Requires an Enterprise plan.                  |

## What each option gives you

Capturing a run as an eval case needs a transcript on the run. Re-running an
eval suite needs Runtype to be able to call the agent. The following table
compares the first two options feature by feature:

| Runtype feature                             | Telemetry in                                                                                                                                                                                                                                                         | Runtype calls the agent                                                                                                                                                                                                |
| ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Runs, Logs, trace tree, cost estimate       | Yes over OTLP. The JSON endpoint `POST /v1/executions/ingest` is an equivalent that produces the same run and trace tree.                                                                                                                                            | Yes                                                                                                                                                                                                                    |
| Transcript on the run                       | Yes when the spans carry conversation content: the `gen_ai.*` content attributes, or a supported framework's own vocabulary ([Send traces from a supported framework](/developer-guides/guides/reporting-external-telemetry#send-traces-from-a-supported-framework)) | Yes                                                                                                                                                                                                                    |
| Capture a run as an eval case               | Yes with a transcript. Flue, the Vercel AI SDK, LangGraph, the OpenAI Agents SDK, and Mastra all send one by default. OpenClaw, Hermes, and the Pi coding agent send one only when you enable content capture. The Pi durable binding sends one by default.          | Yes                                                                                                                                                                                                                    |
| Re-run an eval suite against the agent      | No                                                                                                                                                                                                                                                                   | A2A endpoint: yes, and cases that replay recorded tool calls are skipped ([Running an eval](/user-guide/evals/running-an-eval)). `runtype-stream` endpoint: no. Re-run the scenario yourself and inspect the next run. |
| Persona chat, surfaces, products, schedules | No                                                                                                                                                                                                                                                                   | Yes                                                                                                                                                                                                                    |

## Before you begin

To set up the agent and a telemetry API key in one place, click
**New > Connect an agent** in the dashboard. The **Connect an agent** page
opens. Inside a product, the agent joins that product when its first run
arrives. In a workspace with no agents, the page creates the agent and a test
key for you. Otherwise, you pick or create the agent on that page. If you
choose **Your coding agent**, its setup prompt requests the key and you approve
the request in the browser. If you choose **Yourself**, you create the key in
the **Add your key** step, and the page fills the agent ID and key into an
environment block. For OpenClaw, Hermes, and Pi, set those variables on the agent
process, not in your project's `.env` file.

To set them up separately, complete these steps:

1. Create an agent to own the runs. Runtype files ingested traces under a
   Runtype agent ID. Any agent works as an attribution target. To let Runtype
   call the agent, create it as an `external` agent instead. Copy the
   `agent_...` ID from the agent's page or from the `list_agents` tool.
2. Create a telemetry API key. On the **Settings > API Keys** page, choose the
   **Telemetry Ingest** permission group (`TELEMETRY:WRITE`, append-only). Keys
   with `AGENTS:WRITE` also work. For the steps, see
   [Create a telemetry API key](/developer-guides/guides/reporting-external-telemetry#create-a-telemetry-api-key).

## Send telemetry to Runtype

Pick the section that matches how your agent is built and where it runs.

### Flue on Cloudflare Workers, no code

Workers Observability traces Flue 2 agents deployed with `@flue/vite` and
`@cloudflare/vite-plugin` automatically. It emits an `invoke_agent` span per
run, a `chat` span per model turn, and an `execute_tool` span per tool call.
The spans follow the OpenTelemetry GenAI semantic conventions and include
conversation content by default.

Exporting Workers traces to an OTLP destination requires a Workers Paid plan.

To export those traces to Runtype, follow these steps:

1. In the Cloudflare dashboard, open **Workers Observability** and add a
   destination of type **Traces**.

2. Set the destination endpoint to `https://api.runtype.com/v1/otel/v1/traces`.

3. Add the custom header `Authorization` with the value
   `Bearer rt_YOUR_TELEMETRY_KEY`.

4. Add the custom header `x-runtype-agent-id` with the value
   `agent_YOUR_AGENT_ID`.

   Replace the following:

   * `rt_YOUR_TELEMETRY_KEY`: your telemetry API key
   * `agent_YOUR_AGENT_ID`: the ID of the Runtype agent that owns the runs

5. Reference the destination in `wrangler.jsonc` and sample every request while
   you iterate:

   **`wrangler.jsonc`**

   ```jsonc title="wrangler.jsonc"
   {
     "observability": {
       "traces": {
         "enabled": true,
         "destinations": ["runtype"],
         "head_sampling_rate": 1,
       },
     },
   }
   ```

6. Build and deploy (`npx vite build && npx wrangler deploy`), then run one
   conversation. The run appears under the agent's **Runs** with a transcript,
   so you can capture it as an eval case.

To keep prompts and tool payloads out of the export, register
`instrument(createCloudflareTracing({ content: false }))` from
`@flue/runtime/cloudflare` at module scope in `app.ts`. This also removes the
transcript, and with it eval capture.

For a Cloudflare Agents SDK agent, wrap the AI SDK with `wrapAISDK` and export
through the same kind of Workers Observability destination. For the setup, see
[Cloudflare Agents SDK](/developer-guides/guides/reporting-external-telemetry#cloudflare-agents-sdk).
A separate Cloudflare Agents SDK adapter, `@runtypelabs/cloudflare-agents`, is
in beta and published to npm. It adds durable delivery from your own Worker.
For its setup, see
[Cloudflare Agents SDK adapter](/developer-guides/guides/reporting-external-telemetry#cloudflare-agents-sdk-adapter).

### Flue on Node, Cloud Run, or another host

Flue's `instrument()` accepts either of two instrumentations. Point exactly one
of them at Runtype. Both emit an `invoke_agent` span per run, so exporting both
to the same endpoint doubles token counts and cost.

The following table compares the two instrumentations:

| Instrumentation          | Choose it for                                                                                                                                                                                                                                                   |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `@runtypelabs/flue-otel` | The improvement loop and loop analytics. From 0.5 it sends the transcript, system prompt and tool content by default (each kind can be switched off), so runs can be captured, and it adds an exact iteration count, a run-level stop reason, and summed usage. |
| `@flue/opentelemetry`    | Stock instrumentation. It exports the conversation by default, so runs carry a transcript and can be captured; Runtype derives loop structure from Flue's own attributes, without the exact iteration count or run-level stop reason.                           |

Both need your application to own the OpenTelemetry SDK and exporter. The full
pipeline, attribution options, and flush rules are in
[Instrumenting a Flue agent](/developer-guides/guides/flue-instrumentation).

### Any OpenTelemetry-instrumented agent

Use the stock exporter settings. Runtype accepts OTLP over HTTP, not gRPC:

**`Environment variables`**

```bash title="Environment variables"
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.runtype.com/v1/otel
OTEL_EXPORTER_OTLP_HEADERS=authorization=Bearer rt_YOUR_TELEMETRY_KEY,x-runtype-agent-id=agent_YOUR_AGENT_ID
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
```

Runtype reads `invoke_agent`, `chat`, and `execute_tool` spans anywhere in the
trace (an enclosing HTTP server span is fine) and the GenAI content attributes
for the transcript. For details, attribution precedence, and the JSON
compatibility endpoint, see
[Reporting telemetry from an external agent](/developer-guides/guides/reporting-external-telemetry).

## Let Runtype call your agent

The agent's endpoint must be a public HTTPS URL. Runtype rejects `http:` URLs,
`localhost`, and private IP addresses. To test an agent that runs on your
machine, expose it through a tunnel, such as Cloudflare Tunnel or ngrok, and
register the tunnel URL.

Set `protocol` to `runtype-stream` for an endpoint that streams Runtype events.
Omit `protocol` for an endpoint that speaks A2A JSON-RPC.

### Register the agent

To register an A2A agent in the dashboard, go to the **Agents** page, click
**New agent**, and choose **A2A Agent**. The **Connect A2A Agent** form takes
an optional agent card URL, the agent name, and the A2A endpoint URL. After you
click **Connect Agent**, set the framework and authentication on the agent's
page.

The dashboard form has no protocol setting. To register a `runtype-stream`
agent, send a `POST /v1/agents` request or use the MCP `create_agent` tool,
which takes the same fields as `agent_type` and `external_config`. The following
request body registers a Flue agent:

**`External agent with a runtype-stream endpoint`**

```json title="External agent with a runtype-stream endpoint"
{
  "name": "Support agent (Flue)",
  "agentType": "external",
  "externalConfig": {
    "endpoint": "https://my-worker.example.workers.dev/dispatch",
    "protocol": "runtype-stream",
    "framework": "flue",
    "auth": { "type": "bearer", "credentials": "{{secret:MY_AGENT_TOKEN}}" },
    "timeoutMs": 120000
  }
}
```

The following table describes the `externalConfig` fields:

| Field              | Description                                                                                                                                                             |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `endpoint`         | Required. The URL that Runtype calls.                                                                                                                                   |
| `protocol`         | `runtype-stream` or `a2a`. Defaults to `a2a` when you omit it.                                                                                                          |
| `agentCardUrl`     | A2A only. The agent card URL. Defaults to `/.well-known/agent-card.json` on the endpoint's origin.                                                                      |
| `framework`        | Optional label: `langchain`, `vercel_ai`, `crewai`, `cloudflare`, `autogen`, `flue`, or `custom`.                                                                       |
| `auth.type`        | `none`, `bearer`, `api_key`, or `basic`.                                                                                                                                |
| `auth.credentials` | The token, API key, or base64-encoded `user:pass`. Agent reads return it as `<redacted>`. To change `auth.type`, send new credentials.                                  |
| `auth.headerName`  | For `api_key`, the header that carries the key. Defaults to `X-API-Key`.                                                                                                |
| `timeoutMs`        | 1000 to 300000. Defaults to 30000. The limit covers the connection and reading the whole response stream, so raise it for agent turns that take longer than 30 seconds. |
| `retryCount`       | 0 to 10. Defaults to 3. For a `runtype-stream` endpoint, Runtype retries only the connection, never a stream that has already started.                                  |

For an A2A agent, Runtype fetches the agent card when you register the agent.
The agent must serve its card at `/.well-known/agent-card.json` on the
endpoint's origin, or you must set `agentCardUrl`.

### Connect an agent from the dashboard

On the **Connect an agent** page, the **Flue**, **AI SDK**, **LangGraph**,
**OpenAI Agents SDK**, **Mastra**, **Cloudflare Agents**, and **Any OTel
source** chips support an A2A connection over a public URL or a local listener.
**OpenClaw**, **Hermes**, and **Pi** support telemetry only. The call setup includes the
telemetry setup, so Runtype also receives the agent's runs.

Framework selection does not convert a native framework API into A2A. If your
agent has no A2A server, [add the SDK adapter](#add-an-a2a-adapter). Mastra
serves A2A itself and needs no adapter.

To let Runtype call the agent, follow these steps:

1. Select your framework. Under **What you want**, keep **Let Runtype call
   it**.
2. Under **Who sets it up**, select **Your coding agent** or **Yourself**.
   With **Your coding agent**, the setup prompt also has the coding agent add
   the A2A adapter and run `runtype devices listen` on your machine. With
   **Yourself**, copy the blocks in the **Add the adapter** step.
3. In **Where it runs**, select **On this machine** or **Public URL**. Only you
   can test against your machine. Flows, surfaces, and schedules need a public
   URL.
4. In **Endpoint**, enter the **A2A endpoint**. The page derives the agent card
   URL from it. To change the agent card URL, click **Edit**.
5. For **On this machine**, select the computer that runs the agent in
   **Listener**. If exactly one listener is online, the page selects it. For
   **Public URL**, use HTTPS. If your server requires a bearer token, enter it
   in **Bearer token**.
6. Click **Test**. The result lists the agent's **Name**, **Skills**,
   **Responses**, and **Auth**.
7. Click **Connect agent**. The button appears only after a test succeeds.

If the receiving agent is an `external` agent or has no model or system prompt,
connecting converts it in place. Runtype never converts a configured Runtype
agent. It creates a separate `external` agent with the same name instead.

### Add an A2A adapter

For a JavaScript or TypeScript agent, `createA2AHandler` from `@runtypelabs/sdk`
provides an A2A 0.3 Agent Card and a buffered text-call endpoint. On the
**Connect an agent** page, the **Add the adapter** step shows a JavaScript
adapter and its install command for each framework. The adapter does not start
or control a terminal session.

Install the SDK and a Node HTTP server in your agent project:

**`Install the adapter host`**

```bash title="Install the adapter host"
pnpm add @runtypelabs/sdk @hono/node-server
```

Wrap your existing agent function in a request handler:

**`a2a-server.ts`**

```typescript title="a2a-server.ts"
import { serve } from '@hono/node-server'
import { createA2AHandler } from '@runtypelabs/sdk'
import { runAgent } from './agent'

const handler = createA2AHandler({
  name: 'Support agent',
  url: 'http://127.0.0.1:4111/a2a',
  run: ({ text, contextId, signal }) =>
    runAgent({ text, contextId, signal }),
})

serve({ fetch: handler, port: 4111, hostname: '127.0.0.1' })
```

Replace `runAgent` with your application's function. It must return a string or
a promise of a string. Start the file with your project's TypeScript runner.
The endpoint is `http://127.0.0.1:4111/a2a` and the Agent Card URL is
`http://127.0.0.1:4111/.well-known/agent-card.json`.

Use the callback that matches your framework:

| Framework         | Callback behavior                                                                                                                                                                                                                  |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Flue              | Call `init(YourAgent, { id: contextId })`, dispatch the text, and return `(await handle.read(receipt, { signal })).text`. Start the Flue runtime before serving requests.                                                          |
| AI SDK            | Return the `text` from `generateText({ model, prompt: text, abortSignal: signal })`. Supply your existing model and tools.                                                                                                         |
| LangGraph         | Invoke your compiled graph with a human message and `configurable.thread_id: contextId`. Return the final message's text.                                                                                                          |
| OpenAI Agents SDK | Call `run(agent, text, { signal })` and return its string `finalOutput`.                                                                                                                                                           |
| Cloudflare Agents | In the Worker's `fetch` handler, get the agent with `getAgentByName(env.SupportAgent, contextId)` and return the text from a method on your Agent class that runs one turn. The adapter serves on the `wrangler dev` port, `8787`. |
| Any OTel source   | Call your agent function and return its text response.                                                                                                                                                                             |

These callbacks use JavaScript SDKs. A Python agent needs an A2A server in its
own process. [LangGraph Agent Server](https://docs.langchain.com/langsmith/server-a2a)
provides A2A endpoints. [Mastra's A2A server](https://mastra.ai/docs/connections/a2a)
also provides its own endpoint and Agent Card.

The handler passes the incoming message, request metadata, `contextId`, and
request abort signal to your callback. It stores no conversation history.
For multi-turn conversations, configure your framework's persistent session or
checkpointer using `contextId`. The AI SDK and OpenAI examples are single-turn
until you add that storage. With Flue, cancellation stops waiting for a reply;
it does not stop an already dispatched durable run.

For public deployment, set `url` to your HTTPS endpoint and authenticate POST
requests before they reach the handler. Keep the basic Agent Card public so
Runtype can discover it. Authorize access to each conversation ID in
your application. The adapter does not authenticate requests or authorize
conversations. The request body limit is 1 MiB by default. Streaming, task
lookup, task resumption, and push notifications require a fuller A2A server,
such as the [official A2A JavaScript SDK](https://github.com/a2aproject/a2a-js).

### Connect a local agent

Start your Mastra agent with its A2A endpoint available on
`http://127.0.0.1:4111`. Authenticate the CLI to the same account and workspace
you use in the dashboard.

An API key used by the listener needs `DEVICES:WRITE` to pair and `DEVICES:READ`
to verify an existing device. An API key that registers or calls the agent also
needs `DEVICES:EXECUTE`, alongside the relevant agent permissions.

Keep a terminal open with the listener running:

**`Listen for local agent calls`**

```bash title="Listen for local agent calls"
runtype devices listen --forward-to http://127.0.0.1:4111
```

The command prints a device ID. It forwards calls to the specified loopback
port without publishing a public URL. Stopping the command disconnects the
listener. Changing accounts or workspaces creates a separate device identity.

On the **Connect an agent** page, select your framework, then **On this
machine** in **Where it runs**. Enter the local **A2A endpoint**, for example
`http://127.0.0.1:4111/api/a2a/supportAgent` for Mastra. The endpoint and the
agent card URL must use the same loopback origin and port. In **Listener**,
select the listener with the device ID that the command printed. Only online
listeners with HTTP forwarding appear. Click **Test**, then click **Connect
agent**. The test fetches the agent card through the listener. To do the same
through the API, send `POST /v1/agents/fetch-agent-card` with the card `url`,
`"transport": "device"`, and the `deviceId`.

For API registration, send the device ID with the Mastra endpoint and discovery
URL:

**`Register the local Mastra agent`**

```bash title="Register the local Mastra agent"
curl https://api.runtype.com/v1/agents \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Local support agent",
    "agentType": "external",
    "externalConfig": {
      "framework": "mastra",
      "protocol": "a2a",
      "transport": "device",
      "deviceId": "YOUR_DEVICE_ID",
      "endpoint": "http://127.0.0.1:4111/api/a2a/supportAgent",
      "agentCardUrl": "http://127.0.0.1:4111/api/.well-known/supportAgent/agent-card.json"
    }
  }'
```

Replace `YOUR_API_KEY` with your Runtype API key and `YOUR_DEVICE_ID` with the
printed device ID. Replace `supportAgent` in both URLs with your Mastra agent ID.
If the agent requires authentication, include `externalConfig.auth` as in the
public endpoint example.

For organization-owned agents, use the listener for direct calls authenticated
as the device owner. Flow runs, MCP capability execution, channels, and schedules
do not carry that device authority. Use **Public URL** with an HTTPS origin for
those runs. Public connections use `transport: "http"` and do
not need a device ID. Device transport supports only A2A; the `runtype-stream`
protocol requires a public connection. Local device connections return buffered A2A responses
even when the agent advertises streaming.

Changing the selected device or transport requires submitting authentication
again. If a call ends without confirmation, check the local agent before
manually retrying an action that changes data.

### Implement a runtype-stream endpoint

Runtype sends `POST {endpoint}` with a JSON body and reads a
`text/event-stream` response. The request body has these fields:

* `messages`: the full conversation history, as `{ "role", "content" }`
  objects, on every call.
* `context.conversationId`: present only when the run has a conversation ID.
* `metadata`: an object that includes `source` and `agentId`.

The response must follow these rules:

* The first frame is `execution_start`.
* Text and tool frames come next.
* The last frame is one terminal `execution_complete` or `execution_error`.
  Set its optional `outcome` field to `completed`, `error`, or `aborted`.
* Each frame's SSE `id:` equals its 0-based `seq`.

The event schemas are the `ExecutionStreamEvent` union in the
[API reference](/api-reference). It is the same stream that the Runtype API
emits for its own agents.

A Flue agent needs one route. Run the turn with
`init(agent, { id: conversationId })` and `handle.dispatch(message)`. Then map
each chunk that `handle.read(receipt, { onEvent })` delivers onto the matching
frame. Because the Flue session already keeps its history under
`conversationId`, dispatch only the last user message in `messages`, not the
whole array.

### Call Hermes over A2A

The **Connect an agent** page sets up telemetry for Hermes. Hermes also serves
A2A itself when its `a2a` gateway platform is enabled, by default on port
`9900` with its agent card at `/.well-known/agent-card.json`. Register that
endpoint as an A2A agent, or reach it on your machine through
`runtype devices listen`.

Before you register it, set the following on the Hermes gateway process:

* `A2A_BEARER_TOKEN`: enter the same token as the agent's bearer token in
  Runtype. Without a token, Hermes accepts calls from localhost only.
* `A2A_MAX_PINGPONG_TURNS=20`: Runtype sends the same A2A `contextId` on every
  turn of a conversation, so Hermes keeps its session. Hermes counts the turns
  in each context as loop protection and, by default, rejects the sixth turn.
  `20` is the highest value Hermes accepts.

Hermes resets the count after a conversation is idle for an hour. A busier
conversation fails at turn 21 with `Anti-loop protection: context ... exceeded
20 turns`. Start a new conversation to continue.

### Use the registered agent

After you register the agent, you can test it from its dashboard page, the MCP
`execute_agent` tool, or the SDK. You can also add it to a product as a
capability ([Adding capabilities to a product](/user-guide/products-surfaces/adding-capabilities-to-a-product))
or embed it in a Persona chat widget
([Embedding the chat widget](/user-guide/products-surfaces/embedding-the-chat-widget-script-tag)).

## Run a Runtype agent on your cloud

Exporting an agent requires an Enterprise plan and an API key with the
`RUNTIME:EXPORT` scope.

Export an agent authored in Runtype with
`runtype deploy --agent <agent-id> --target cloudflare`, or use the
`cloud-run` or `vercel` target. The `export_agent_runtime` tool also exports
the agent. Host it with `@runtypelabs/runtime`.

Set `RUNTYPE_API_KEY` on the deployment to a telemetry API key
(`TELEMETRY:WRITE`). The runtime then reports every run to the `/v1/otel`
endpoint with no instrumentation of your own. Each run carries its trace tree,
transcript, tool arguments and results, token usage, and cost estimate.

`RUNTYPE_TELEMETRY=off` stops reporting, and `RUNTYPE_TELEMETRY_CONTENT=off`
reports runs without conversation content. For setup, the hosted
`/v1/runtime/*` endpoints, and PII redaction, see
[Self-hosting (BYOC)](/developer-guides/guides/self-hosting).

## The improvement loop for an external agent

[Improving your agents from production traces](/developer-guides/guides/improving-agents-from-production)
describes the loop for hosted agents. For an external agent, two steps change:

* **Fix** happens in your repository, not with `update_agent`.
* **Verify** with `run_eval_suite` only when Runtype can call the agent over
  A2A. Otherwise, re-run the scenario against your redeployed agent and inspect
  the resulting run. The captured cases remain the regression record.

Runs ingested over telemetry show an **external** badge in the Runs view. Their
timings and cost are what the agent reported, not what Runtype metered.

## Troubleshooting

Use the following checks when telemetry does not arrive as expected:

* Verify the key and endpoint with an empty export. The response is `200` with
  `{}`. A `403` means the key has neither `TELEMETRY:WRITE` nor `AGENTS:WRITE`.

  **`cURL smoke test`**

  ```bash title="cURL smoke test"
  curl -X POST https://api.runtype.com/v1/otel/v1/traces \
    -H "Authorization: Bearer rt_YOUR_TELEMETRY_KEY" \
    -H "Content-Type: application/json" \
    -d '{"resourceSpans": []}'
  ```

* A run that stays in flight never received its closing `invoke_agent` span.
  Flush the provider before the process exits or the handler returns.

* `missing_agent_attribution` means Runtype found no agent ID for the trace.
  Set the `runtype.agent.id` resource attribute or the `x-runtype-agent-id`
  header.

* `ambiguous_agent_attribution` means the trace's spans claim more than one
  agent. Export each agent invocation as its own trace, or set one
  `runtype.agent.id`.

* An attributed trace with no Run has no GenAI operation, model or inference
  signal, or recognized Runtype execution telemetry. Runtype retains it as a
  diagnostic in **Logs**.

* A `429` or `503` response is temporary. Retry the export after the
  `Retry-After` delay. OpenTelemetry exporters do this by default.

* Doubled tokens and cost mean two instrumentations export to the same endpoint.

* A tool card that reads "No tool content in this trace" means content capture
  is off on the producer.

* A `partial_success` response means Runtype rejected some traces in the export
  and stored the rest. The message names the rejected traces and their reasons.

Use the following checks when Runtype cannot call your agent:

* `Failed to fetch agent card` or `Agent card response is not JSON` when you
  register an A2A agent means Runtype could not load the card. Serve it as JSON
  at `/.well-known/agent-card.json` on the endpoint's origin, or set
  `agentCardUrl`.
* `Failed to connect to external agent` from a `runtype-stream` endpoint means
  the endpoint failed URL validation, returned a non-2xx status, or did not
  answer within `timeoutMs`. Check the HTTPS and public-address rules, the
  `auth` settings, and the timeout.
* `External agent stream is not conformant` means a frame broke the stream
  rules. The bracketed code in the message names the rule.
* `External agent stream ended without a terminal event` means the stream
  closed before `execution_complete` or `execution_error`.
* A `runtype-stream` response fails if it exceeds 20,000 events, 8 MiB in
  total, or 1 MiB in one frame. Runtype does not retry a stream that fails partway through.
* `Anti-loop protection: context ... exceeded 5 turns` from a Hermes agent
  means its turn limit is at the default. Set `A2A_MAX_PINGPONG_TURNS=20` on
  the Hermes gateway, as described in [Call Hermes over A2A](#call-hermes-over-a2a).

## Next steps

* [Instrumenting a Flue agent](/developer-guides/guides/flue-instrumentation): the Node pipeline, attribution, and tool-content options.
* [Reporting telemetry from an external agent](/developer-guides/guides/reporting-external-telemetry): the generic OTLP contract.
* [Self-hosting (BYOC)](/developer-guides/guides/self-hosting): export a Runtype agent and run it on your own cloud.
* [Improving your agents from production traces](/developer-guides/guides/improving-agents-from-production): the capture-fix-verify loop.
* [Connect Runtype to coding agents](/developer-guides/integrations/connect-coding-agents): run the loop from Claude Code, Cursor, or Codex.