Fork an agent totry another path.
Cut an agent's journal at any finished turn and wake the copy with a different prompt, model or message. The original is never touched, so you can go back to a bad turn, or clone an agent mid-task, without risking the run you have.
What a fork is
An agent's journal already records every model call, tool call and result, each with a position called seq. A fork copies that journal up to a seq you choose into a branch with a name. You wake the branch like any agent by passing memory: { "fork": "try-b" }. It starts with everything the agent knew at the cut and carries on from there on its own.
You save nothing ahead of time. Every finished turn is already a place you can fork from, and cutting a fork is a database copy that costs nothing and makes no model call. The original agent gets no new events, memory or wakes, which makes a fork safe to use on an agent that is in production.
You can cut only at a turn boundary: after an event where no model call is in flight and no tool call is waiting for its result. GET /v1/agents/{agent}/fork-points lists them.
Where can I cut?
curl -sS "$BASE/v1/agents/support-bot/fork-points" \
-H "Authorization: Bearer $ARGENTIC_API_KEY"
# { "agent": "support-bot", "points": [12, 19, 31, 44], "next_after": null }Debug from a past turn
A support agent told a customer the wrong thing at turn 3 of a long run. You want to see whether a different system prompt, or a different model, would have gone better, using the same history the agent had at that moment.
1. Find the cut. Each wake lists where it started and ended in the journal. Pick the end of the wake before the bad answer, or read the journal with ?kind=trigger to see where each message arrived. Check the number against fork-points; if it is not one, take the nearest point below it.
Wake positions
curl -sS "$BASE/v1/agents/support-bot/wakes" \
-H "Authorization: Bearer $ARGENTIC_API_KEY"
# each wake has journal: { from_seq, trigger_seq, to_seq }2. Cut the fork. Name it for what you are testing. Sending the same name and cut again is safe and answers 200 with already: true. The same name with a different cut is a 409.
Cut a fork
curl -sS -X POST "$BASE/v1/agents/support-bot/fork" \
-H "Authorization: Bearer $ARGENTIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "try-b", "to_seq": 31 }'
# 201 { "agent": "support-bot", "fork": "try-b", "memory": { "fork": "try-b" }, "to_seq": 31, "events": 31 }3. Wake the fork with the change. system, role and model are not remembered between calls, so changing them on a fork wake is just sending different values. The wake runs on your model key and counts toward your limits like any other.
Same history, other prompt
curl -sS "$BASE/v1/agents/support-bot/wake" \
-H "Authorization: Bearer $ARGENTIC_API_KEY" \
-H "X-Model-Key: $MODEL_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "The customer says the refund never arrived. What do you do?",
"system": "You are a support agent. Check the refund status before you promise anything.",
"model": { "provider": "openai", "id": "gpt-4.1", "prices": { "in": 2, "out": 8 } },
"memory": { "fork": "try-b" }
}'
# the answer carries fork: { name, answered_from_record, no_record }, and no actions4. Read what happened. Pass memory=fork:try-b on the journal, wakes, memory and replay routes to look at the fork instead of the agent. The first block below shows only what the fork wrote after the cut.
Read the fork
# only what the fork wrote after the cut
curl -sS "$BASE/v1/agents/support-bot/journal?memory=fork:try-b&after=31" \
-H "Authorization: Bearer $ARGENTIC_API_KEY"
# what the fork remembers
curl -sS "$BASE/v1/agents/support-bot/memory?memory=fork:try-b" \
-H "Authorization: Bearer $ARGENTIC_API_KEY"The tools are the part to read carefully. A fork never calls your servers. When the fork's agent makes a tool call, the engine answers it from what the original recorded: the same tool with the same arguments gets the recorded result, in order. If the new prompt sends the agent down a path the original never took, there is no record to answer from, and the agent gets an error result saying so. The answer counts those in fork.no_record and the rest in fork.answered_from_record. A no_record above zero tells you the change altered what the agent tried to do, which is often the thing you were looking for.
Run as many forks as you like from the same cut and compare their journals side by side. Replay is the cheaper check for a different question: it rebuilds what the model saw on each turn and calls no model.
Clone an agent mid-task
Cut at the newest turn boundary and the fork holds everything the agent holds right now: its decisions, facts, plans and conversation. That is a clone of the agent with the same context, and it is the way to run several attempts at one decision in parallel without disturbing the real run.
Say an agent is partway through a migration plan and you want three approaches costed before it commits. Find the last point in fork-points (follow next_after until it is null), then cut one fork per approach.
The newest point
# the newest turn boundary: follow next_after to the last page, take the last point
curl -sS "$BASE/v1/agents/support-bot/fork-points?after=44&limit=500" \
-H "Authorization: Bearer $ARGENTIC_API_KEY"Three clones (Fish)
for name in plan-a plan-b plan-c
curl -sS -X POST "$BASE/v1/agents/support-bot/fork" \
-H "Authorization: Bearer $ARGENTIC_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"name\": \"$name\", \"to_seq\": 44}"
endWake each clone with its own message, such as "Cost this by moving the data first" for plan-a and "Cost it by moving the traffic first" for plan-b. Different forks run independently and in parallel. Each fork takes one wake at a time, like any agent. Compare the replies, pick one, and send that approach to the original agent as an ordinary message. The original is the agent whose actions your app carries out, so the decision lands where the work happens.
A clone can think and answer but it cannot act. It gets no actions back, a tool declared with approval or mandate is refused on a fork with a 400, and results is refused because there is nothing to answer. Plans a fork copied from the original do not show in GET /v1/due and set no alarm, so a clone never wakes itself.
What a fork does
- The original is never written to. The fork's journal is a copy of it up to the cut, with the same
seqvalues, payloads and timestamps, and its own events after that. - A real thing the original did stays done. A refund that was sent is still sent, and a fact the agent stored is still in the original's memory. A fork doesn't change either. It only lets you try something else from the point you cut.
- If you copy a fork's reply into something real, such as a message to a customer, that is your act and not the fork's.
- Budget approvals are off on a fork: a fork wake that reaches a cap ends with the cap status.
- You can change the message, system prompt, role and model on a fork wake. You can't yet swap a recorded tool result for a different one.
Cost and limits
| Thing | What to expect |
|---|---|
| Cutting a fork, listing forks, listing fork points | Free. No model call, and not a call against your plan. |
| Waking a fork | A wake like any other: your model key, your spend limits, and one call against your plan. |
| Spend | The copied events are not counted as spend a second time. The daily cap and monthly budget count each model call once. |
| How many | 50 forks per account. Ask us for more. |
| Size | A journal of up to 100,000 events at the cut. The copy counts toward your run-history size, and a copy that would pass the cap is refused with a 507. |
| Deleting | There is no delete. A fork's events are its spend record, and a delete route would be a way round the daily cap. An idle fork goes to cold storage like any journal. |
| Not yet | Forking a fork, forking a shared memory, and forks inside schedules or hooks. |
The three routes, with every field and status, are in the API reference.