The four identifiers
In practice:
- A vanilla follow-up turn uses
chat_id(orexecution_id) alone to identify the conversation. - A branching turn additionally uses
from_checkpoint_idto specify where in the conversation to resume. - The
checkpoint_idyou store today is what becomes afrom_checkpoint_idtomorrow.
persistence_mode
The Research Agent request accepts persistence_mode with two values. Both
allow multi-turn follow-ups; they differ in how long the conversation
survives.
disabled(default) — creates an ephemeral conversation thread. Follow-up turns against the samechat_idwork for approximately one hour. After that, the underlying checkpoint is evicted and a resume attempt returns HTTP 404 with the detail"Cannot follow up: chat expired".enabled— creates a persistent conversation thread. Thechat_idandcheckpoint_idreturned inCOMPLETEcan be used to continue or branch the conversation indefinitely.
"persistence_mode mismatch") and HTTP 409 on Workflows.
Choosing a mode: leave the default disabled for one-off questions
and short sessions that wrap up within an hour. Set enabled on turn 1
when the conversation needs to survive longer — branching workflows,
research threads spanning a working session, anything you want a user to
resume the next day.
Workflows expose persistence_mode on the synchronous execute endpoint
with the same semantics (default disabled, immutable, mismatch returns
409). A submitted workflow is always stored — the stored run is how you read
its result once the submit request has returned — so the asynchronous
endpoint does not accept persistence_mode.
The "INITIAL" sentinel
Passing from_checkpoint_id: "INITIAL" is a special instruction that truncates
the entire conversation and starts over with the new message. Use it when the
user explicitly says “let’s start over” or when you detect a context-poisoning
mistake (a typo in a name, a wrong company id, a stale assumption) that you want
to clear cleanly while keeping the same chat_id.
"INITIAL" deletes all prior interactions for that chat_id. There is no undo.
Worked example: multi-turn conversation
The pattern below persistschat_id and checkpoint_id between turns, letting the
user ask follow-up questions on top of the agent’s previous answers.
chat_id returned by each event identifies the conversation thread; the
checkpoint_id returned in COMPLETE is your cursor for the next turn. Persist
both server-side or in client storage if you need conversations to survive across
sessions.
Branching a conversation
To “rewind” a conversation — for example, when the user edits an earlier message or wants to explore a different follow-up question from a known good state — pass thecheckpoint_id from the turn you want to resume from, with the same
chat_id. Any interactions after that checkpoint are discarded, and the new
message takes their place.
ckpt_after_turn_1 no longer exist on the server.
Workflows: resuming by execution_id
Workflows do not expose checkpoints. Instead, each execution has an execution_id
that the streaming response carries on every event. Pass that id back in a later
request to continue the same conversation.
execution_id continues the conversation but does not
re-run the original template’s research plan from scratch; the agent treats the
new input (or follow-up message) as a continuation, with prior interactions in
context.
What a resume is allowed to do depends on whether the run was stored:
- Not stored (
persistence_modeat its default ofdisabled): theexecution_idcontinues the conversation, as in the example above. - Stored (
persistence_mode: "enabled", and always for a run submitted toPOST /v1/workflow/execute/async): theexecution_idresumes a run that stopped before it finished, so the same run can pick up from where it stopped. A run that is stillpendingorrunningcannot be continued, and neither can one that alreadycompleted. Both return409. To ask a new question, submit a new run. See Running a workflow.
Common pitfalls
- Treating the default
disabledmode as suitable for long sessions. Ephemeral threads expire after about an hour; a follow-up that arrives later returns HTTP 404 with detail"Cannot follow up: chat expired". Setpersistence_mode: "enabled"on turn 1 if the conversation needs to survive longer than that. - Switching modes mid-conversation. The mode is immutable once a conversation is created. Resume requests must pass the same value used on turn 1 or the server returns HTTP 400 (Research Agent) / 409 (Workflows).
- Storing
chat_idbut notcheckpoint_id. Achat_idalone is enough for the simple “continue the latest turn” case, but if you want stable replay or branching, you also need thecheckpoint_idreturned inCOMPLETE. - Mixing
chat_idbetween users. Thechat_idis a server-side conversation identifier; do not share it across users or sessions you want isolated. - Treating
checkpoint_idas introspectable. It is an opaque string; do not parse, compare, or derive meaning from its contents. - Expecting a stored workflow run to accept a follow-up turn. For a stored
run,
execution_idresumes a run that stopped early. An in-progress run and a completed run both return HTTP 409. - Calling
from_checkpoint_id: "INITIAL"accidentally. It deletes the entire thread. Guard this code path behind an explicit user action.
Next steps
Streaming responses
Reference for every message type, including how
checkpoint_id arrives in COMPLETE.Error handling
Handle 404s from invalid checkpoint IDs and other continuity failures.