Skip to navigation

Bring your own agent

Your agent can keep running where it runs today, whether you built it with Flue, 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:

OptionWhat you doWhat it unlocks
Send telemetry to RuntypeExport OpenTelemetry traces to https://api.runtype.com/v1/otelThe 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 agentRegister an external agent whose endpoint streams Runtype events (runtype-stream) or speaks A2ATesting 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 cloudExport an agent authored in Runtype and host it with @runtypelabs/runtime (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 featureTelemetry inRuntype calls the agent
Runs, Logs, trace tree, cost estimateYes over OTLP. The JSON endpoint POST /v1/executions/ingest is an equivalent that produces the same run and trace tree.Yes
Transcript on the runYes when the spans carry conversation content: the gen_ai.* content attributes, or a supported framework’s own vocabulary (Send traces from a supported framework)Yes
Capture a run as an eval caseYes 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 agentNoA2A endpoint: yes, and cases that replay recorded tool calls are skipped (Running an eval). runtype-stream endpoint: no. Re-run the scenario yourself and inspect the next run.
Persona chat, surfaces, products, schedulesNoYes

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.

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
    {
    "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. 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.

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:

InstrumentationChoose it for
@runtypelabs/flue-otelThe 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/opentelemetryStock 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.

Any OpenTelemetry-instrumented agent

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

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.

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
{
"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:

FieldDescription
endpointRequired. The URL that Runtype calls.
protocolruntype-stream or a2a. Defaults to a2a when you omit it.
agentCardUrlA2A only. The agent card URL. Defaults to /.well-known/agent-card.json on the endpoint’s origin.
frameworkOptional label: langchain, vercel_ai, crewai, cloudflare, autogen, flue, or custom.
auth.typenone, bearer, api_key, or basic.
auth.credentialsThe token, API key, or base64-encoded user:pass. Agent reads return it as <redacted>. To change auth.type, send new credentials.
auth.headerNameFor api_key, the header that carries the key. Defaults to X-API-Key.
timeoutMs1000 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.
retryCount0 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. 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
pnpm add @runtypelabs/sdk @hono/node-server

Wrap your existing agent function in a request handler:

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:

FrameworkCallback behavior
FlueCall init(YourAgent, { id: contextId }), dispatch the text, and return (await handle.read(receipt, { signal })).text. Start the Flue runtime before serving requests.
AI SDKReturn the text from generateText({ model, prompt: text, abortSignal: signal }). Supply your existing model and tools.
LangGraphInvoke your compiled graph with a human message and configurable.thread_id: contextId. Return the final message’s text.
OpenAI Agents SDKCall run(agent, text, { signal }) and return its string finalOutput.
Cloudflare AgentsIn 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 sourceCall 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 provides A2A endpoints. Mastra’s A2A server 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.

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
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
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. 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) or embed it in a Persona chat widget (Embedding the chat widget).

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).

The improvement loop for an external agent

Improving your agents from production traces 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
    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.

Next steps