Importing conversations
Move a transcript from another system into Runtype so an Agent’s next turn continues where the old system left off. The transcript is stored on the conversation, and the turn after the import replays it server-side.
The transcript shape
POST /v1/conversations and PUT /v1/conversations/{id} accept a messages array.
Each message is:
author records who wrote the message in the system you are migrating from, so a
role: "user" turn stays attributable to a specific person. It is stored and returned
as you sent it, and it is never replayed to a model.
The following POST /v1/conversations body imports a conversation whose assistant turn
called a tool, with the tool result in place, so the next turn can replay the whole
exchange:
Tool pairs must be complete
Every model provider rejects a half-finished tool exchange, so Runtype rejects one at write time rather than at the next turn:
- A
toolmessage must carry at least onetoolResultsentry. - Every assistant
toolCallsentry must be answered by atoolmessage before the next non-tool message. - No result may answer an unknown
toolCallId, and notoolCallIdmay be answered twice. toolCallsis legal only on an assistant message;toolResultsonly on a tool message.
A transcript that breaks any of these answers 400:
If the source system does not record tool calls, import the text turns only. A text-only transcript is always valid.
An imported reasoning part is stored, but it is left out of the replay: a model’s
thinking signature is bound to the provider that produced it, so a foreign one would
fail the next turn.
Binding an agent and recording provenance
agentId binds the conversation to one Agent and must name an Agent you own; anything
else answers 404. source records where the transcript came from
({ system, externalId?, importedAt? }). Both are stored on the conversation’s
metadata as metadata.agentId and metadata.importSource.
Re-running an import is safe
source.externalId is an idempotency key. If a conversation with the same
source.system and source.externalId already exists, POST /v1/conversations does
not create a second one: it answers 200 with the existing conversation and
"imported": false. A fresh create answers 201 with "imported": true. A unique
index backs this, so two POSTs racing each other still produce exactly one
conversation.
Use the source system’s own conversation id as externalId and a migration script can
be restarted from the top without duplicating anything. A create without
source.externalId is never deduplicated.
Importing a long transcript in chunks
PUT /v1/conversations/{id} takes messagesMode:
replace(the default) swaps the whole stored transcript for what you send.appendadds your messages to the end of the stored transcript.
In append mode a message whose id the conversation already stores is skipped, so
re-sending a chunk after a timeout adds nothing. Give every message the source
system’s message id and each chunk becomes retryable on its own.
The combined transcript, stored plus incoming, is validated as one transcript. A tool
message in this chunk may answer a toolCallId the conversation already stores; a
result answering nothing is still rejected with 400
CONVERSATION_TRANSCRIPT_INVALID. Because the combined transcript is checked whole,
keep an assistant toolCalls message and the tool message answering it in the
same chunk: a chunk that ends on an unanswered tool call is rejected, exactly as it
would be in replace mode.
The recipe for a large migration: POST /v1/conversations with source and the first
chunk, then PUT each later chunk with messagesMode: "append", in order.
Size limits
A conversation is one row, and the limits are checked against the transcript the write would leave stored, so an append is measured against stored plus incoming:
A write that would exceed any of these answers 400 and changes nothing. Split a
transcript that does not fit across several conversations, or drop the oldest turns.
What import does not do
An import is a write, not a run. It does not fire surface webhooks, start an Agent turn, or meter execution usage. Only Runtype’s own product analytics record that the conversation was created or updated.
Two things the transcript itself does not carry, and where they live instead:
- Memory. Long-term facts an Agent should already know are seeded through the
memory API (
POST /v1/runtime/memory/save, read back withPOST /v1/runtime/memory/recall), not throughmessages. Those routes are an Enterprise-plan feature. - Attachments. Send them inline as
imageorfilecontent parts carrying base64 data, and a large one is offloaded to asset storage automatically as the write lands; or reference an asset you have already uploaded with anasset_refpart. Runtype does not fetch attachment URLs for you.
Continuing the conversation
The turn after an import calls POST /v1/agents/{id}/execute with history: "stored",
the conversationId returned by the import, and only the new user message. The server
replays the stored transcript ahead of the delta, so the imported tool pairs reach the
provider. The delta is appended to the stored transcript when the turn is admitted, so
a failed turn still keeps the question, and the Agent’s reply is appended once the turn
completes. A turn that pauses (a client-side tool, or a detached run) appends no reply.
With the default history: "inline", the caller keeps owning the transcript and sends
the whole messages array on every turn.
POST /v1/dispatch takes the same history field, alongside agent.agentId and
conversationId:
A stored-history dispatch needs a saved Agent: an inline agent, a flow dispatch, a
Claude Managed agent, Prefer: respond-async, a join, and a resume or client-tool
continuation are all refused with STORED_HISTORY_UNSUPPORTED. As on the execute
route, a turn that pauses or goes detached appends no reply: the question is stored,
the answer is not, so read the reply off the execution’s events and, if you need it in
the transcript, append it yourself with messagesMode: "append".
Dispatch messages also accept the tool role with toolCalls and toolResults, in
the same shape the transcript uses, so a caller keeping history: "inline" can still
replay full-fidelity tool turns on every request.
Next steps
Continue with these guides:
- Creating and configuring agents: set up the Agent that an imported conversation is bound to.
- Agent tools: give the Agent the tools an imported transcript’s tool pairs refer to.
- What are Agents?: how Agents use tools, system prompts and conversation history.