Join an active agent execution
Use x-runtype-concurrency: join to send another message while your saved native
Runtype agent is working. A compatible active conversation accepts that input
into the same execution. An idle conversation starts an execution.
Choose the admission policy that matches your application:
Join is opt-in. The default remains reject. Join requires a conversationId
and a supported native Runtype agent. Use POST /v1/dispatch or
POST /v1/agents/{agentId}/execute. Unsupported agents return an explicit error.
For client tools, use the saved-agent execute endpoint because dispatch doesn’t
support client tools.
Send only additional user messages
Keep the same agent, conversation, tenant/end-user scope, credential authority,
and execution options on every send. Send only additional user messages, not
the full transcript or replacement system instructions. Don’t combine join
with x-runtype-coalesce.
Set RUNTYPE_API_KEY to your Runtype API key. Use a different idempotency key
for each logical message. To send additional input asynchronously with the
TypeScript SDK, use this example:
Replace agent_YOUR_AGENT_ID with your saved native agent’s ID.
The Python example uses a dedicated client for this message. This keeps its idempotency header separate from other sends:
Replace agent_YOUR_AGENT_ID with your saved native agent’s ID.
To send the same input with cURL, use this request:
Replace agent_YOUR_AGENT_ID with your saved native agent’s ID.
The TypeScript SDK also accepts admission options on the
client.agents.execute method. Streaming dispatch accepts those options in its
request initialization argument.
Track the delivery, not a separate execution
An asynchronous response includes executionId, deliveryId, deliveryStatus,
and deliveryStatusUrl. Multiple deliveries can name the same execution.
Poll the returned delivery URL with the same authorization. With the TypeScript
SDK, you can use the client.executions.getDelivery(executionId, deliveryId)
method.
Use the delivery state to distinguish acceptance from application:
pending: Runtype stored the input but hasn’t incorporated it into the execution’s checkpoint.applied: Runtype durably incorporated the input into the execution’s context. This doesn’t promise that the model follows the message.settled: Applied input shares the execution’s terminaloutcome, including failure or cancellation.not_applied: The execution ended before using the input. Runtype doesn’t restart it automatically.
Retry the same message with the same idempotency key to recover its delivery
receipt. If you reuse that key with different messages or execution options,
Runtype returns DELIVERY_IDEMPOTENCY_CONFLICT. Receipt retention is bounded;
receipts aren’t a permanent transcript archive.
Without Prefer: respond-async, blocking and server-sent events (SSE) callers
follow the execution’s answer. SSE carries X-Runtype-Delivery-Id and replays
the execution’s event stream. Merge events by execution identity and event
cursor to avoid displaying the same answer twice.
Safe boundaries and limits
Review these constraints before accepting additional input in your application:
In-flight calls and pauses
Input follows first-in, first-out order and can’t overtake an earlier queue-only request. A model call or tool batch in flight finishes first. Runtype checkpoints completed tool results before applying additional input.
Runtype doesn’t withdraw streamed output or roll back external side effects. Approval, client-tool, and elicitation pauses remain paused. Text can’t approve a tool or replace a structured resume response.
Completion races and execution budgets
Joining doesn’t reset turn, time, cost, recovery, or pause-expiry limits. It doesn’t reserve a second execution. Several messages can share one answer.
After completion seals the active inbox, later input starts or queues a
separate execution with ordinary admission and quotas. If a queued join
follows a failed or cancelled execution, its input becomes not_applied.
Capacity, scope, and conversation history
Each delivery accepts one to 16 user messages and at most 64 KB. Runtype
bounds pending deliveries and retained receipts. If the inbox is full,
Runtype returns DELIVERY_INBOX_FULL.
DELIVERY_CHECKPOINT_FULL refuses a send that exceeds the execution’s
checkpoint capacity, including pending input. This refusal doesn’t interrupt
the execution. Later model or tool output can still exhaust storage limits.
An execution that exceeds the checkpoint size limit can refuse joins with
JOIN_HOST_UNSUPPORTED. Accepted input requires durable checkpointing.
JOIN_CONFIG_MISMATCH means the active execution has different options,
data scope, or credential authority. Don’t retry by dropping security context.
Wait for the execution to finish or use a different conversation.
Later delta-only executions reuse retained successful conversation history.
If Runtype can’t use that history safely, JOIN_HISTORY_UNAVAILABLE requires
a different conversation.
Join from a browser client
Browser clients use a separate protocol: POST /v1/client/chat with
submitMode: "join", not the management API’s concurrency header. Never put a
Runtype management API key in browser code.
Enable durableTurns.enabled on the chat surface and bind a native Runtype
agent. Initialize with the surface’s public client token and
durableRecovery: true. Retain the visitor token returned by /v1/client/init
and initialize again with that visitor token and session ID to claim the
conversation. Only offer joining when init returns durableRecovery.join: true.
A history-disabled surface can still support durable recovery and joining.
Each browser send contains exactly one additional user message. Its id
must equal the request’s turnId. Keep that ID and the entire payload unchanged
when retrying an uncertain send; mint a new ID for a genuinely new message.
Don’t resend the transcript, alter execution options, or replace structured tool
answers with chat text.
Use the apiUrl, session ID, and visitor token from your client configuration
and init flow. The browser supplies its Origin header; the surface must allow
that origin.
Handle both successful admission shapes:
- 200 SSE: A new host streams its answer. Retain both
X-Runtype-Execution-IdandX-Runtype-Delivery-Idresponse headers. - 202 JSON: Retain
executionId,deliveryId,deliveryStatus,deliveryStatusUrl, andeventsUrl. Keep an existing stream and any open tool question intact when this receipt names the same host. An uncertain initial start can also return this recovery handle; attach toeventsUrlwhen you don’t already follow that execution.
Read the returned delivery URL with ?sessionId=... and the same
X-Visitor-Token header. Execution event attachment uses the same credentials;
retain the SSE cursor to avoid displaying replayed events twice. Browser
recovery URLs are scoped under
/v1/client/conversations/{conversationId}/executions/{executionId}. A different
visitor or conversation cannot use the receipt.
Disconnecting, reloading, or aborting the HTTP stream does not cancel the
execution. An explicit Stop action sends POST to
/v1/client/conversations/{conversationId}/executions/{executionId}/cancel?sessionId=...
with the visitor header. Cancellation can leave pending inputs not_applied;
only an explicit new send should resubmit them. Joining must not cancel the
host, close an elicitation, or consume a tool’s pending resume response.
Next steps
Use these guides to configure tools and preserve caller scope:
- Working with tools: configure the work your agent can perform.
- End-user identity and credentials: preserve scope and credential authority across sends.