Browse the reference
Agent · /v1/agent
Stacks of proposals
A stack is a chain of proposals, each built on the one below it, so a large change can be reviewed in parts that each make sense on their own. Every layer is an ordinary draft with an ordinary proposal, held to the same review rules as any other.
Layers publish from the bottom. When one lands, the layers above it are rebuilt on what landed. A stack is at most eight layers deep.
#7 Document the authentication system
SScribe·0 of 3 published·on main
The layers
Add a layer- 3scribe/auth-keysPendingOpen2 documents0/1 approved
on scribe/auth-sessions — layer 2, needing 1 more approval
Move downFold downDrop - 2scribe/auth-sessionsPendingOpen5 documents0/1 approved
on scribe/auth-overview — layer 1, needing 1 more approval
Move downMove upFold downDrop - 1scribe/auth-overviewPendingApprovedNext to publish3 documents1/1 approved
on main — the trunk
Publish this layerMove upDrop
resting on main
Stacks
A stack is addressed by its number within the Concept, and a layer by its position. Position 1 is the bottom, resting on the trunk.
List stacks
GET/v1/agent/c/{identifier}/stacks
Every stack in the Concept, each with its layers.
curl https://your-server.example/v1/agent/c/k7m2xq9d/stacks \
-H "Authorization: Bearer arb_live_…"{
"items": [
{ "number": 7, "title": "Document the authentication system", "state": "open", "drifted": false, … }
]
}Create a stack
POST/v1/agent/c/{identifier}/stacks
Makes a stack, empty or from drafts that already exist. Given drafts, the first becomes the bottom layer, each rests on the one before it, and a proposal is opened for every layer.
Body
- titlestring · 1 to 500 · required
- What the whole change is.
- descriptionstring · up to 20,000
- More about it.
- draftsstring[] · up to 8
- Drafts that exist already, bottom first.
Refusals
- 404not_foundA draft named does not exist.
- 400validation_failedMore than eight drafts.
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/stacks \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{
"title": "Document the authentication system",
"drafts": ["scribe/auth-overview", "scribe/auth-sessions"]
}'{
"number": 7,
"title": "Document the authentication system",
"description": null,
"state": "open",
"drifted": false,
"trunkBranch": "main",
"owner": { "type": "API_KEY", "id": "…", "name": "Scribe", "agentSlug": "scribe" },
"layers": [
{
"position": 1,
"draft": "scribe/auth-overview",
"baseBranch": "main",
"proposalNumber": 51,
"proposalState": "approved",
"state": "pending",
"documentsChanged": 3,
"approvals": 1,
"requiredApprovals": 1,
"conflicts": [],
"lastRestackedAt": "2026-10-09T09:12:00.000Z"
},
{
"position": 2,
"draft": "scribe/auth-sessions",
"baseBranch": "scribe/auth-overview",
"proposalNumber": 52,
"proposalState": "open",
"state": "pending",
"documentsChanged": 5,
"approvals": 0,
"requiredApprovals": 1,
"conflicts": [],
"lastRestackedAt": "2026-10-09T09:12:00.000Z"
}
],
"createdAt": "2026-10-08T15:30:00.000Z",
"updatedAt": "2026-10-09T09:12:00.000Z"
}Read a stack
GET/v1/agent/c/{identifier}/stacks/{n}
One stack, and every layer with where its proposal stands.
What a layer tells you
- statepending · blocked · conflicted · published · dropped
- Where the layer stands in the stack. Blocked means a layer beneath it conflicted, so this one has not been looked at yet.
- proposalStatestring
- Where its proposal stands in review.
- baseBranchstring
- The draft it rests on: the layer below, or the trunk.
And the stack
- stateopen · partially_published · published · closed
- How much of it has landed.
- driftedboolean
- Whether its drafts no longer rest on one another the way the stack says they do. See the end of this page.
curl https://your-server.example/v1/agent/c/k7m2xq9d/stacks/7 \
-H "Authorization: Bearer arb_live_…"{
"number": 7,
"title": "Document the authentication system",
"description": null,
"state": "open",
"drifted": false,
"trunkBranch": "main",
"owner": { "type": "API_KEY", "id": "…", "name": "Scribe", "agentSlug": "scribe" },
"layers": [
{
"position": 1,
"draft": "scribe/auth-overview",
"baseBranch": "main",
"proposalNumber": 51,
"proposalState": "approved",
"state": "pending",
"documentsChanged": 3,
"approvals": 1,
"requiredApprovals": 1,
"conflicts": [],
"lastRestackedAt": "2026-10-09T09:12:00.000Z"
},
{
"position": 2,
"draft": "scribe/auth-sessions",
"baseBranch": "scribe/auth-overview",
"proposalNumber": 52,
"proposalState": "open",
"state": "pending",
"documentsChanged": 5,
"approvals": 0,
"requiredApprovals": 1,
"conflicts": [],
"lastRestackedAt": "2026-10-09T09:12:00.000Z"
}
],
"createdAt": "2026-10-08T15:30:00.000Z",
"updatedAt": "2026-10-09T09:12:00.000Z"
}Shaping it
Every change to a stack's shape answers with what it did to the layers above the change: which were rebuilt, which were already current, and where it stopped if one conflicted. Shaping a stack needs the standing to change it, which is its owner's or an editor's.
Add a layer on top
POST/v1/agent/c/{identifier}/stacks/{n}/layers
Cuts a new draft from the top layer and opens its proposal. Write on the new draft as on any other.
Body
- draftstring · required
- The new draft's name, as a namespace and a name.
- titlestring · 1 to 500 · required
- Its proposal's title.
- descriptionstring · up to 20,000
- Its proposal's description.
Refusals
- 409stack_too_deepThe stack already has eight layers.
- 409draft_existsA draft already has that name.
- 402plan_limitA layer is a draft, and the agent's owner has as many open drafts as the plan allows.
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/stacks/7/layers \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{
"draft": "scribe/auth-keys",
"title": "Document API keys"
}'{
"number": 7,
"layers": [
{ "position": 1, "draft": "scribe/auth-overview", … },
{ "position": 2, "draft": "scribe/auth-sessions", … },
{ "position": 3, "draft": "scribe/auth-keys", "baseBranch": "scribe/auth-sessions", "proposalNumber": 53, … }
],
…
}Insert a layer
POST/v1/agent/c/{identifier}/stacks/{n}/layers/insert
Slots a new layer in at a position. Everything there and above moves up one, the layer that was there now rests on the new one, and the layers above are rebuilt.
Body
- positioninteger · 1 to 8 · required
- Where it goes.
- draft, title, descriptionstring
- As for adding a layer on top.
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/stacks/7/layers/insert \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{
"draft": "scribe/auth-cookies",
"title": "Document session cookies",
"position": 2
}'{
"restacked": [2, 3],
"skipped": [1],
"conflictedAt": null,
"blocked": [],
"conflicts": []
}Reorder the layers
PATCH/v1/agent/c/{identifier}/stacks/{n}/layers/order
Puts the layers in a new order and rebuilds them on one another in that order.
Body
- orderinteger[] · 1 to 8 · required
- The current positions, listed bottom first in the order wanted.
curl -X PATCH https://your-server.example/v1/agent/c/k7m2xq9d/stacks/7/layers/order \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{ "order": [2, 1, 3] }'{
"restacked": [2, 3],
"skipped": [1],
"conflictedAt": null,
"blocked": [],
"conflicts": []
}Fold a layer into the one below
POST/v1/agent/c/{identifier}/stacks/{n}/layers/{pos}/fold
Joins a layer with the one below it, so the two changes become one, and everything above rests on the result. For when two parts turn out to be one idea.
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/stacks/7/layers/3/fold \
-H "Authorization: Bearer arb_live_…"{
"restacked": [2, 3],
"skipped": [1],
"conflictedAt": null,
"blocked": [],
"conflicts": []
}Drop a layer
DELETE/v1/agent/c/{identifier}/stacks/{n}/layers/{pos}
Takes a layer out of the stack. The layer above it now rests on the layer below, and the layers above are rebuilt from there.
curl -X DELETE https://your-server.example/v1/agent/c/k7m2xq9d/stacks/7/layers/2 \
-H "Authorization: Bearer arb_live_…"{
"restacked": [2, 3],
"skipped": [1],
"conflictedAt": null,
"blocked": [],
"conflicts": []
}Read what one layer changes
GET/v1/agent/c/{identifier}/stacks/{n}/layers/{pos}/diff
One layer's change against the layer it rests on, so a reviewer sees only that layer's part.
curl https://your-server.example/v1/agent/c/k7m2xq9d/stacks/7/layers/2/diff \
-H "Authorization: Bearer arb_live_…"{
"base": "scribe/auth-overview",
"head": "scribe/auth-sessions",
"documents": [ { "kind": "added", "path": "auth/sessions.md", "additions": 64, … } ],
"totals": { "documents": 5, "additions": 212, "deletions": 3 },
"nextCursor": null
}Keeping it current
Restacking rebuilds each layer on the one beneath it. Each layer is saved on its own, a layer that is already current is skipped, and it stops at the first conflict. Layers above a conflict are blocked and left as they were.
- layer 4blocked
- layer 3conflicted
- layer 2restacked
- layer 1skipped
- main
- Layer 4. Not looked at. Waits on layer 3.
- Layer 3. Stopped here. The conflicts come back.
- Layer 2. Rebuilt on layer 1, and saved.
- Layer 1. Already current. Nothing to do.
Restack
POST/v1/agent/c/{identifier}/stacks/{n}/restack
Rebuilds the layers on one another. The body may be left out, which restacks from the bottom.
Body
- fromPositioninteger · 1 to 8
- The lowest layer to rebuild. 1 when absent.
What comes back
- restacked, skippedinteger[]
- Layers rebuilt, and layers that were already current.
- conflictedAt, conflictsinteger · null, array
- The layer where it stopped, and what collided there.
- blockedinteger[]
- Layers above it, not yet looked at.
Refusals
- 409stack_driftedThe drafts no longer rest on one another as the stack says. Repair the drift first.
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/stacks/7/restack \
-H "Authorization: Bearer arb_live_…"{
"restacked": [2],
"skipped": [1],
"conflictedAt": 3,
"blocked": [4],
"conflicts": [
{
"id": "…",
"documentId": "…",
"path": "auth/keys.md",
"kind": "body",
"frontmatterKey": null,
"base": "…",
"theirs": "…",
"mine": "…",
"resolved": false
}
]
}Layers 1 and 2 are current. Layer 3 conflicts. Layer 4 waits on it.
Catch the whole stack up
POST/v1/agent/c/{identifier}/stacks/{n}/sync
Catches the bottom layer up with the trunk, then restacks everything above it: the one call for “the trunk has moved on”.
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/stacks/7/sync \
-H "Authorization: Bearer arb_live_…"{
"restacked": [2, 3],
"skipped": [1],
"conflictedAt": null,
"blocked": [],
"conflicts": []
}Publishing
A layer is never published through its own proposal. It goes through the stack, from the bottom, so the layers above are rebuilt on what landed. Each layer is held to the Concept's review rules separately, and stacking relaxes none of them.
Publish the bottom layer
POST/v1/agent/c/{identifier}/stacks/{n}/layers/{pos}/merge
Publishes one layer to the trunk and rebuilds the rest on it. Needs an editor's standing.
Refusals
- 409not_mergeableThe layer does not meet the review rules. The problem says which.
- 409merge_conflictThe trunk moved and the layer now conflicts. The problem carries the conflicts.
- 409stack_driftedRepair the drift first.
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/stacks/7/layers/1/merge \
-H "Authorization: Bearer arb_live_…"{
"merged": true,
"trunkSeq": 319,
"documentsMerged": 3,
"cascade": {
"restacked": [2, 3],
"skipped": [],
"conflictedAt": null,
"blocked": [],
"conflicts": []
}
}cascade is what happened to the layers above as they were rebuilt on what landed.
Publish several layers together
POST/v1/agent/c/{identifier}/stacks/{n}/merge
Publishes the bottom layers together as one entry in the trunk's history, so the lot can be reverted in one step. If any of them conflicts, none is published.
Body
- layersinteger · 1 to 8
- How many, from the bottom. All of them when absent.
- messagestring · up to 1,000
- The history entry's message.
Refusals
- 409not_mergeableA layer does not meet the review rules.
- 409merge_conflictA layer conflicts, so nothing was published. The problem names it in failedAtLayer.
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/stacks/7/merge \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{ "layers": 2, "message": "Document sessions and their overview" }'{
"merged": true,
"layersMerged": 2,
"trunkSeq": 320,
"documentsMerged": 8,
"failedAtLayer": null
}Dissolve the stack
POST/v1/agent/c/{identifier}/stacks/{n}/unstack
Ends the stack and keeps the work. Each layer's proposal carries on as an ordinary proposal, rebased on the trunk.
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/stacks/7/unstack \
-H "Authorization: Bearer arb_live_…"{ "layers": 3 }Drift
A stack says which draft each layer rests on. If a draft is moved some other way, the two disagree, and that is drift. Publishing or restacking a drifted stack is refused, because it would merge against a base the work was never written on. Reading still works.
Check for drift
GET/v1/agent/c/{identifier}/stacks/{n}/drift
Whether the stack and its drafts agree and, if not, where they part.
curl https://your-server.example/v1/agent/c/k7m2xq9d/stacks/7/drift \
-H "Authorization: Bearer arb_live_…"{
"diverged": true,
"layer": 2,
"expectedBase": "scribe/auth-overview",
"actualBase": "main",
"options": ["adopt", "restore", "dissolve"]
}Repair drift
POST/v1/agent/c/{identifier}/stacks/{n}/drift/resolve
There are three ways out, and the server never picks one, because which is right depends on what was meant.
Body
- actionadopt · restore · dissolve · required
adopttakes the drafts as they really are and reorders the stack to match.restoreputs the stack's own order back and restacks.dissolveends the stack and keeps each proposal.
nextAction, and lists the three choices with where the drafts part.curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/stacks/7/drift/resolve \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{ "action": "restore" }'{
"action": "restore",
"restacked": {
"restacked": [2, 3],
"skipped": [1],
"conflictedAt": null,
"blocked": [],
"conflicts": []
}
}