Route a busy message automatically
Use auto when you don’t know whether a message sent mid-run belongs to the
work in progress. While a durable native agent turn is running, a classifier
compares the running request with the incoming message and picks one route:
- Steer: The message joins the running execution at the next safe
boundary, like
x-runtype-concurrency: steer. Corrections, constraints, extra facts, and changes of approach usually steer. - Queue: The message runs as its own turn after the current one finishes,
like
x-runtype-concurrency: queue. A clearly independent task usually queues.
When nothing is running, an auto message is an ordinary turn. Runtype makes
no classifier call and charges nothing for routing. auto is opt-in. The
default admission policy stays reject.
Before you begin
auto routes only where steering
works. You need the following:
- A saved native Runtype agent that runs durable turns.
- A
conversationIdon every send. - New user messages only, not the full transcript.
Send a message with auto routing
From a server, send x-runtype-concurrency: auto on POST /v1/dispatch or
POST /v1/agents/{agentId}/execute. The body has the same shape as a steer:
one or more new user messages for the same agent and conversation. Use a
different idempotency key for each logical message.
To send a message with the TypeScript SDK and read where it went, use this example:
Replace agent_YOUR_AGENT_ID with your saved native agent’s ID.
The Python example uses a dedicated client, so its admission headers apply only to this message:
To send the same message with cURL and print the response headers, use this request:
Replace agent_YOUR_AGENT_ID with your saved native agent’s ID, and set
RUNTYPE_API_KEY to your Runtype API key. Don’t send Prefer: respond-async
with auto: an asynchronous request keeps its 202 handle and isn’t routed.
Send a message with auto routing from a browser
Browser clients send submitMode: "auto" on POST /v1/client/chat. The setup
and the request shape are the same as for submitMode: "steer": enable durable
turns, send exactly one user message, and set that message’s id to the
request’s turnId. For the full browser setup, see
Steer from a browser client.
The following example sends one message with auto routing:
While a turn runs, a steered message returns 202 with the steer delivery
receipt. A queued message returns 202 with the queued execution’s handle.
When nothing is running, the message starts a turn and streams its answer.
Retry an uncertain send with the same turn ID and message ID.
Read the routing decision
When a turn is running, or auto cannot route on this request, the answer
reports where the message went. An auto message that arrives while nothing is
running is an ordinary turn and carries no report. The following table lists the
response headers:
The source takes one of these values:
classifier: The classifier answered, and its answer chose the route.timeout: The classifier didn’t answer in time. The message queued.error: The classifier call failed. The message queued.unanswered: The classifier returned no usable answer. The message queued.abstained: The decision model declined to answer. The message queued.circuit_open: Runtype skipped the classifier after repeated failures. The message queued.disabled: Routing didn’t run, because the agent’s mode isoffor the request can’t be routed.
A JSON response body repeats the decision as a busyMessageRoute object with
route, source, and, for a classifier answer, probability and model.
Streamed responses carry the headers only. The following excerpt shows a
steered message’s receipt, with its other fields omitted:
The object also carries steerUnavailable: true when the classifier chose
steer but the running turn could no longer accept input. In that case the
message queued instead, and route is queue. If the running turn finished
while the classifier was deciding, the message runs as the next turn. Neither
route loses the message.
Configure routing on the agent
The agent’s config.busyMessageRouting object controls the classifier. Every
field is optional:
mode: How Runtype applies the decision.enforced(default): The decision routes the message.off: Every busyautomessage queues without a classifier call. The answer reports sourcedisabled.
classifierModel: The decision model that classifies the message. Use anidfromGET /v1/decision-models. Runtype rejects any other value when you save the agent. It defaults to Jev (typesafe-ai/jev). A model that needs your own provider key, such as OpenAI Decisions, fails without one, and those messages queue with sourceerror.
The following agent configuration routes with Clef Flash:
Runtype bills each classifier call to your organization, in the same way as other model usage. A message sent while nothing is running makes no call.
Fallbacks and limits
Review these behaviors before you rely on auto routing:
Timeouts and repeated failures
The classifier has 3 seconds to answer, including one quick retry after a provider server error. A timeout or an error queues the message, because queueing never changes work that’s already running.
After three timeouts or provider server errors in a row for one classifier
model on one credential, Runtype stops calling that model for 60 seconds.
Models on Runtype’s platform credentials share one count across
organizations, because a provider outage affects all of them. Your own
provider key has its own count. Credential and spending errors never
count.
During that time, busy auto messages queue at once with source
circuit_open. After the pause, Runtype lets one call through. An answer
resumes normal routing, and another failure starts a new pause.
A second message while one waits
A conversation holds one waiting message. A second auto message sent
while the first is still being classified, or is already queued, returns
409 with code CONVERSATION_QUEUE_FULL. Retry it after the waiting
message starts.
Requests that auto can't route
auto never returns a 4xx error because a request can’t be routed.
It keeps the lane’s own behavior and reports source disabled instead.
On the server API, the following requests keep the admission they would
get without the x-runtype-concurrency header:
- A request that runs in process, or that targets a Claude Managed agent.
- A request that runs a flow.
- A request without a
conversationId. - A request with
history: "stored". - A request with
x-runtype-coalesce: true. - A request with
Prefer: respond-async. It still gets its202handle.
A server API request whose messages can’t steer, such as a full transcript, queues when the conversation is busy.
On POST /v1/client/chat, a request that can’t be routed runs as
submitMode: "normal". That includes a surface without durable turns and
a request that isn’t one user message whose id equals turnId.
Next steps
- Steer an active agent execution: delivery receipts, error codes, and browser setup.
- Concurrency modes across agent frameworks: compare
autowith other admission policies. - Creating and configuring agents: set
busyMessageRoutingon a saved agent.