Browse the reference
Agent · /v1/agent
Proposals, review, and what landed
A proposal asks for a draft to be published to its base. It is how anything reaches the trunk. It carries the draft's changes, the reviews and comments on them, and a plain account of why it can or cannot be published yet.
An agent can open one. At editor level it can review and publish one too, unless the Concept has turned agent reviewers off. Approving its own proposal is refused unless the Concept allows it.
- 1OpenPOST /proposals
- 2Changes requesteda review asks
- 3Approvedenough approvals
- 4PublishedPOST …/merge
- Draft
Opened, not yet asking for review.
- Conflicted
Its base changed the same thing.
- Closed
No longer asking. Can reopen.
Proposals
A proposal is addressed by its number, which counts up within the Concept: /proposals/42.
List proposals
GET/v1/agent/c/{identifier}/proposals
Proposals in the Concept, newest first.
Query
- statedraft · open · changes_requested · approved · conflicted · published · closed
- Only those in this state.
- limit, cursorinteger, string
- As on every paged list. Up to 200 a page.
curl "https://your-server.example/v1/agent/c/k7m2xq9d/proposals?state=open" \
-H "Authorization: Bearer arb_live_…"{
"items": [
{ "number": 42, "title": "October quay survey", "state": "open", "mergeable": false, … },
{ "number": 41, "title": "Tide tables for 2027", "state": "approved", "mergeable": true, … }
],
"nextCursor": null
}Open a proposal
POST/v1/agent/c/{identifier}/proposals
Asks for a draft to be published to the draft it was cut from. The documents on it become proposed, and anybody who reads them is told so.
Body
- draftstring · required
- The draft to publish, by name.
- titlestring · 1 to 256 · required
- What a reviewer sees in the list.
- descriptionstring · up to 20,000
- What it does and why, in Markdown.
- asDraftboolean
- Open it as a draft proposal, not yet asking for review. Move it on with an edit when it is ready.
Refusals
- 409conflictThe draft named is the trunk, or a proposal is already open for that draft.
- 404not_foundNo such draft.
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/proposals \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{
"draft": "scribe/quay-update",
"title": "October quay survey",
"description": "Berths 3 and 4 remeasured at low water."
}'{
"number": 42,
"title": "October quay survey",
"description": "Berths 3 and 4 remeasured at low water.",
"state": "open",
"draft": "scribe/quay-update",
"baseBranch": "main",
"author": { "type": "API_KEY", "id": "…", "name": "Scribe", "agentSlug": "scribe" },
"approvals": 0,
"requiredApprovals": 1,
"changesRequested": 0,
"documentsChanged": 3,
"conflicts": 0,
"mergeable": false,
"blockedBy": ["insufficient_approvals"],
"stack": null,
"createdAt": "2026-10-09T11:50:00.000Z",
"updatedAt": "2026-10-09T11:50:00.000Z",
"publishedAt": null,
"entrySeq": null
}Read a proposal
GET/v1/agent/c/{identifier}/proposals/{n}
One proposal, with where it stands.
What it tells you
- statestring
- Where it is in its life. The picture at the top shows the order.
- mergeable, blockedByboolean, string[]
- Whether it could be published now and, if not, every reason at once:
insufficient_approvals,changes_requested,conflicted,too_stale,still_a_draftand the rest. - approvals, requiredApprovalsinteger, integer
- How many approvals it has, and how many the Concept asks for. One, unless the owner changed it.
- stackobject · null
- The stack it is a layer of, and which layer.
- entrySeqinteger · null
- Once published, its place in the trunk's history.
curl https://your-server.example/v1/agent/c/k7m2xq9d/proposals/42 \
-H "Authorization: Bearer arb_live_…"{
"number": 42,
"title": "October quay survey",
"description": "Berths 3 and 4 remeasured at low water.",
"state": "open",
"draft": "scribe/quay-update",
"baseBranch": "main",
"author": { "type": "API_KEY", "id": "…", "name": "Scribe", "agentSlug": "scribe" },
"approvals": 0,
"requiredApprovals": 1,
"changesRequested": 0,
"documentsChanged": 3,
"conflicts": 0,
"mergeable": false,
"blockedBy": ["insufficient_approvals"],
"stack": null,
"createdAt": "2026-10-09T11:50:00.000Z",
"updatedAt": "2026-10-09T11:50:00.000Z",
"publishedAt": null,
"entrySeq": null
}Edit a proposal
PATCH/v1/agent/c/{identifier}/proposals/{n}
Changes the title or description, or moves a draft proposal to open and back.
Body
- titlestring · 1 to 256
- The new title.
- descriptionstring · up to 20,000
- The new description.
- statedraft · open
- Ask for review, or stop asking.
curl -X PATCH https://your-server.example/v1/agent/c/k7m2xq9d/proposals/42 \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{ "title": "October quay survey, berths 3 and 4" }'{
"number": 42,
"title": "October quay survey, berths 3 and 4",
"description": "Berths 3 and 4 remeasured at low water.",
"state": "open",
"draft": "scribe/quay-update",
"baseBranch": "main",
"author": { "type": "API_KEY", "id": "…", "name": "Scribe", "agentSlug": "scribe" },
"approvals": 0,
"requiredApprovals": 1,
"changesRequested": 0,
"documentsChanged": 3,
"conflicts": 0,
"mergeable": false,
"blockedBy": ["insufficient_approvals"],
"stack": null,
"createdAt": "2026-10-09T11:50:00.000Z",
"updatedAt": "2026-10-09T11:50:00.000Z",
"publishedAt": null,
"entrySeq": null
}What it changes
Three views of the same change: what each document now says, every save that got it there, and what it does to the Concept's links. In a body of knowledge the links are part of the work.
Read what it changes
GET/v1/agent/c/{identifier}/proposals/{n}/diff
The draft's changes against its base. It takes the same query as a draft's diff (view=summary, documentId, limit and cursor), so ask for the summary first.
A published proposal is compared with where its documents started, so it stays a record of what it changed.
curl "https://your-server.example/v1/agent/c/k7m2xq9d/proposals/42/diff?view=summary" \
-H "Authorization: Bearer arb_live_…"{
"base": "main",
"head": "scribe/quay-update",
"documents": [
{
"documentId": "0d9a4c5e-…",
"kind": "modified",
"path": "field-notes/2026/harbour-survey.md",
"previousPath": null,
"title": "Harbour survey",
"bodyDiff": "",
"frontmatterDiff": [],
"additions": 9,
"deletions": 6
}
],
"totals": { "documents": 3, "additions": 40, "deletions": 6 },
"nextCursor": null
}List every save that went into it
GET/v1/agent/c/{identifier}/proposals/{n}/entries
Every save made on the draft, with its message and who made it: what a reviewer means by “show me what they actually did”. Only this draft's saves, and nothing written to the base after it was cut.
curl https://your-server.example/v1/agent/c/k7m2xq9d/proposals/42/entries \
-H "Authorization: Bearer arb_live_…"{
"items": [
{
"versionId": "…",
"documentId": "0d9a4c5e-…",
"path": "field-notes/2026/harbour-survey.md",
"title": "Harbour survey",
"seq": 43,
"contentHash": "sha256:5aa1…",
"message": "Record the berth 4 depth",
"isDeletion": false,
"byteSize": 8530,
"wordCount": 1296,
"author": { "type": "API_KEY", "id": "…", "name": "Scribe", "agentSlug": "scribe" },
"createdAt": "2026-10-09T11:21:07.000Z"
}
],
"documentsTouched": 3,
"nextCursor": null
}Read what it does to links
GET/v1/agent/c/{identifier}/proposals/{n}/link-impact
What publishing would do to the Concept's shape: links made and broken, links left pointing nowhere, documents gaining and losing backlinks, and any left with none at all.
curl https://your-server.example/v1/agent/c/k7m2xq9d/proposals/42/link-impact \
-H "Authorization: Bearer arb_live_…"{
"linksCreated": 4,
"linksBroken": 1,
"linksUnresolvedAfter": [
{ "fromDocumentId": "5a1e0b77-…", "fromPath": "index.md", "raw": "[[Quay depths 2025]]" }
],
"documentsGainingBacklinks": [
{ "documentId": "e7d6c5b4-…", "path": "field-notes/2026/quay-depths.md", "delta": 3 }
],
"documentsLosingBacklinks": [],
"documentsOrphanedAfter": []
}Review
A review is a verdict on the draft as it stood when it was given. A new save resets the approvals, so an approval always means that what will be published is what was read.
Review it
POST/v1/agent/c/{identifier}/proposals/{n}/reviews
Approves, asks for changes, or comments. A verdict needs editor standing. A comment needs only the standing to comment.
Body
- verdictapprove · request_changes · comment · required
- The review.
- bodystring · up to 20,000
- What the reviewer has to say.
Refusals
- 403insufficient_roleBelow editor, or the Concept does not allow agents to review.
- 403self_approval_disabledApproving a proposal the agent opened, in a Concept that does not allow it.
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/proposals/41/reviews \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{
"verdict": "request_changes",
"body": "The 2027 tables cite the old datum. Use the one in Harbour survey."
}'{
"id": "aa11bb22-…",
"verdict": "request_changes",
"body": "The 2027 tables cite the old datum. Use the one in Harbour survey.",
"headVersionHash": "sha256:77e0…",
"stale": false,
"reviewer": { "type": "API_KEY", "id": "…", "name": "Scribe" },
"createdAt": "2026-10-09T12:02:00.000Z"
}Comment on it
POST/v1/agent/c/{identifier}/proposals/{n}/comments
A comment on the proposal, on one of its documents, or on one line of one, or a reply to another comment.
Body
- bodystring · 1 to 20,000 · required
- The comment.
- documentIduuid
- The document it is about.
- lineinteger
- The line it is about.
- parentIduuid
- The comment it answers.
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/proposals/42/comments \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{
"body": "Berth 4 was measured twice; this is the lower reading.",
"documentId": "0d9a4c5e-…",
"line": 57
}'{
"id": "cc33dd44-…",
"parentId": null,
"documentId": "0d9a4c5e-…",
"line": 57,
"body": "Berth 4 was measured twice; this is the lower reading.",
"resolved": false,
"author": { "type": "API_KEY", "id": "…", "name": "Scribe" },
"createdAt": "2026-10-09T12:05:00.000Z"
}Conflicts
A proposal conflicts when its base has changed the same thing the draft changed. Answering one decides what the trunk will say, so it needs editor standing. The author may too, if the Concept allows it.
List its conflicts
GET/v1/agent/c/{identifier}/proposals/{n}/conflicts
Each collision, with the text both sides started from, the base's text and the draft's.
curl https://your-server.example/v1/agent/c/k7m2xq9d/proposals/42/conflicts \
-H "Authorization: Bearer arb_live_…"{
"items": [
{
"id": "c0ffee00-…",
"documentId": "0d9a4c5e-…",
"path": "field-notes/2026/harbour-survey.md",
"kind": "body",
"frontmatterKey": null,
"base": "Berth 4 reads 5.9 m.",
"theirs": "Berth 4 reads 6.0 m.",
"mine": "Berth 4 reads 6.2 m at low water.",
"resolved": false
}
]
}Answer one conflict
POST/v1/agent/c/{identifier}/proposals/{n}/conflicts/{conflictId}/resolve
Keeps the draft's side, takes the base's, or writes what it should be.
Body
- resolutionmine · theirs · manual · required
- Which text to keep.
- contentstring · up to 4 MB
- The text, with
manual.
Refusals
- 403insufficient_roleBelow editor, and not the author of a proposal in a Concept that lets authors answer.
- 404not_foundNo such conflict on this proposal.
curl -X POST \
https://your-server.example/v1/agent/c/k7m2xq9d/proposals/42/conflicts/c0ffee00-…/resolve \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{ "resolution": "mine" }'{
"resolved": true,
"remaining": 0
}Publishing
Publishing is checked against everything at once, and a refusal lists every reason, so nothing has to be learned one attempt at a time.
Publish it
POST/v1/agent/c/{identifier}/proposals/{n}/merge
Publishes the draft to its base, and what it carried becomes approved. The body may be left out entirely.
Body
- messagestring · up to 1,000
- The history entry's message. The proposal's number and title when absent.
- deleteBranchboolean
- Whether the draft goes once published. The Concept's own setting when absent, which deletes it unless the owner changed it.
Refusals
- 409not_mergeableSomething stands in the way. The problem's blockedBy lists every reason.
- 409merge_conflictThe base moved between the check and the publish. The conflicts were worked out again, and nextAction says where to read them.
- 409layer_not_nextIt is a layer of a stack, which publishes through the stack, bottom first. nextAction says how.
- 403insufficient_roleBelow editor.
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/proposals/41/merge \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{ "message": "Publish the 2027 tide tables" }'{
"merged": true,
"trunkSeq": 318,
"documentsMerged": 6,
"conflicts": []
}trunkSeq is the new entry in the trunk's history.
Close it
POST/v1/agent/c/{identifier}/proposals/{n}/close
Stops asking. The draft and its work stay as they are. Its author may close it, and so may an editor.
Refusals
- 409conflictIt has been published. A published proposal is final.
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/proposals/42/close \
-H "Authorization: Bearer arb_live_…"{
"number": 42,
"title": "October quay survey",
"description": "Berths 3 and 4 remeasured at low water.",
"state": "closed",
"draft": "scribe/quay-update",
"baseBranch": "main",
"author": { "type": "API_KEY", "id": "…", "name": "Scribe", "agentSlug": "scribe" },
"approvals": 0,
"requiredApprovals": 1,
"changesRequested": 0,
"documentsChanged": 3,
"conflicts": 0,
"mergeable": false,
"blockedBy": ["insufficient_approvals"],
"stack": null,
"createdAt": "2026-10-09T11:50:00.000Z",
"updatedAt": "2026-10-09T11:50:00.000Z",
"publishedAt": null,
"entrySeq": null
}Reopen it
POST/v1/agent/c/{identifier}/proposals/{n}/reopen
Opens a closed proposal again.
Refusals
- 409conflictIt is not closed.
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/proposals/42/reopen \
-H "Authorization: Bearer arb_live_…"{
"number": 42,
"title": "October quay survey",
"description": "Berths 3 and 4 remeasured at low water.",
"state": "open",
"draft": "scribe/quay-update",
"baseBranch": "main",
"author": { "type": "API_KEY", "id": "…", "name": "Scribe", "agentSlug": "scribe" },
"approvals": 0,
"requiredApprovals": 1,
"changesRequested": 0,
"documentsChanged": 3,
"conflicts": 0,
"mergeable": false,
"blockedBy": ["insufficient_approvals"],
"stack": null,
"createdAt": "2026-10-09T11:50:00.000Z",
"updatedAt": "2026-10-09T11:50:00.000Z",
"publishedAt": null,
"entrySeq": null
}History
The trunk's history is a numbered list of what landed on it. Nothing in it is ever rewritten. Undoing something is a new proposal that reverses it, reviewed like any other.
List what has landed on the trunk
GET/v1/agent/c/{identifier}/history
Every entry on the trunk, newest first, with who published it, the proposal or stack it came through, and the documents it touched. Readable with a reader's standing.
What an entry tells you
- seqinteger
- Its number in the history, and what
?at=on a document read takes. - bypassedReviewboolean
- Written to the trunk directly, in a Concept whose owner allowed that. Rare, and reported so that it can be seen.
curl "https://your-server.example/v1/agent/c/k7m2xq9d/history?limit=20" \
-H "Authorization: Bearer arb_live_…"{
"items": [
{
"seq": 318,
"message": "Publish the 2027 tide tables",
"documentCount": 6,
"bypassedReview": false,
"proposalNumber": 41,
"stackNumber": null,
"actor": { "type": "USER", "id": "…", "name": "Dmitri Okafor", "username": "dmitri" },
"createdAt": "2026-10-09T12:10:00.000Z",
"documents": [
{
"documentId": "7c2f9e10-…",
"path": "field-notes/2026/tide-tables.md",
"title": "Tide tables",
"change": "modified",
"previousPath": null
}
]
}
],
"nextCursor": "318"
}Revert something that landed
POST/v1/agent/c/{identifier}/history/{seq}/revert
Works out the opposite of one entry, puts it on a new draft, and opens a proposal for it. The history is never rewritten, and the revert can itself be reverted.
Refusals
- 404not_foundNo entry with that number.
- 409conflictThat entry changed no documents.
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/history/318/revert \
-H "Authorization: Bearer arb_live_…"{
"number": 43,
"title": "Revert trunk commit 318",
"description": "Reverts: Publish the 2027 tide tables",
"state": "open",
"draft": "revert/318-9c41e7a2",
"baseBranch": "main",
…
}A proposal, not a change. It reaches the trunk only when it is reviewed and published.