Skip to content
Takeida
Browse the reference

Agent · /v1/agent

Read and write documents

A document is Markdown at a path. It is read from a draft, and the trunk is the draft read when none is named. Whatever is read comes back with its provenance: which draft it was read from, and whether that makes it approved, proposed or unapproved.

Reads are built so an agent spends in proportion to what it needs. A read returns no body unless it is asked for. The outline says where a section starts, and a range of lines fetches only that.

Name the draft on every write

?draft=dmitri/field-notes. Without it a write is aimed at the trunk, and is refused.

Say what you read

Send expectedVersion, the version.seq you read. If the document has changed since, the write is refused instead of overwriting.

At most 500 KB of text

A body is measured as the editor measures it, and compared with stats.size.max.

Reading

Every route here takes ?draft= to read from a draft, and reads the trunk without it. A document the agent has been excluded from answers as though it were not there.

List documents

GET/v1/agent/c/{identifier}/documents

One page of a draft's documents in path order, without bodies. lineCount is in every row because it is what decides the next call: read the whole thing, or a range of it.

Query

draftstring
The draft to list. The trunk when absent.
pathstring
A path prefix. field-notes/ lists that folder and everything under it.
limitinteger · 1 to 200
50 when absent.
cursorstring
nextCursor from the page before.
A row may be a stub of id, path and excluded: true: a document the agent has been shut out of. It keeps its place so that paging stays even.
Request
curl "https://your-server.example/v1/agent/c/k7m2xq9d/documents?path=field-notes/&limit=50" \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "items": [
    {
      "id": "0d9a4c5e-7b1f-4c7a-9a10-3f6b2f1c8e21",
      "path": "field-notes/2026/harbour-survey.md",
      "title": "Harbour survey",
      "frontmatter": {},
      "version": { "id": "…", "seq": 41, "contentHash": "sha256:9f2c…" },
      "provenance": {
        "concept": "coastal-atlas",
        "draft": "main",
        "isTrunk": true,
        "approvalStatus": "approved",
        "proposal": null,
        "stack": null
      },
      "author": { "type": "USER", "id": "…", "name": "Ana Reyes", "username": "ana" },
      "updatedAt": "2026-09-28T13:05:44.000Z",
      "wordCount": 1284,
      "lineCount": 96,
      "aliases": ["Harbour"],
      "color": null
    }
  ],
  "nextCursor": null,
  "pending": []
}

Read a document

GET/v1/agent/c/{identifier}/documents/{id}

One document. The body is not sent unless it is asked for: a long document can cost a reading agent its whole context to answer a question about one paragraph, and the outline and a range of lines answer most of those for a fraction of it.

Query

includebody
Send the body too.
draftstring
The draft to read from. The trunk when absent.
atinteger
A point in the trunk's history, by sequence number. Reads the document as it stood then, including one that has since been deleted. It reads history and changes nothing.

What comes back

version.seqinteger
The version read. Send it back as expectedVersion when writing.
stats.sizeobject
length against max is how full the document is, in the unit the limit is enforced in. byteSize is the stored size, for showing and for budgeting a download.

Refusals

  • 404not_foundNo such document on that draft, or the agent has been excluded from it.
Request
curl "https://your-server.example/v1/agent/c/k7m2xq9d/documents/0d9a4c5e-…?include=body" \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "id": "0d9a4c5e-7b1f-4c7a-9a10-3f6b2f1c8e21",
  "path": "field-notes/2026/harbour-survey.md",
  "title": "Harbour survey",
  "body": "The north quay was resurveyed in…",
  "frontmatter": { "tags": ["survey"] },
  "version": { "id": "…", "seq": 41, "contentHash": "sha256:9f2c…" },
  "provenance": {
    "concept": "coastal-atlas",
    "draft": "main",
    "isTrunk": true,
    "approvalStatus": "approved",
    "proposal": null,
    "stack": null
  },
  "stats": {
    "wordCount": 1284,
    "backlinkCount": 7,
    "outgoingLinkCount": 12,
    "size": { "length": 8420, "max": 512000, "byteSize": 8466 },
    "lineCount": 96
  },
  "attachments": [
    {
      "id": "…",
      "filename": "north-quay.png",
      "mimeType": "image/png",
      "byteSize": 482113,
      "width": 1600,
      "height": 900
    }
  ],
  "mentions": [],
  "author": { "type": "USER", "id": "…", "name": "Ana Reyes", "username": "ana" },
  "updatedAt": "2026-09-28T13:05:44.000Z"
}

Without include=body, body is null and everything else is the same.

Read its outline

GET/v1/agent/c/{identifier}/documents/{id}/map

The shape of a document without its text: headings with the line each starts on, named blocks, comments, embeds and tags. The cheap way to decide what to read next.

What comes back

headings[].lineinteger
Where the section starts. Pass it to the next endpoint.
headings[].pathstring
The heading with its ancestors, which is what an edit at a heading is aimed with.
blocks[].blockIdstring
A block that has been given a name, with the lines it spans.
Request
curl https://your-server.example/v1/agent/c/k7m2xq9d/documents/0d9a4c5e-…/map \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "id": "0d9a4c5e-7b1f-4c7a-9a10-3f6b2f1c8e21",
  "path": "field-notes/2026/harbour-survey.md",
  "title": "Harbour survey",
  "headings": [
    { "ordinal": 0, "level": 1, "text": "Harbour survey", "slug": "harbour-survey", "path": "Harbour survey", "line": 1 },
    { "ordinal": 1, "level": 2, "text": "North quay", "slug": "north-quay", "path": "Harbour survey/North quay", "line": 14 },
    { "ordinal": 2, "level": 2, "text": "Tides", "slug": "tides", "path": "Harbour survey/Tides", "line": 58 }
  ],
  "blocks": [
    { "blockId": "berth-table", "kind": "table", "lineStart": 22, "lineEnd": 31, "text": "| Berth | Depth |…" }
  ],
  "comments": [],
  "embeds": [],
  "tags": ["survey"],
  "frontmatter": { "tags": ["survey"] },
  "wordCount": 1284,
  "provenance": { "draft": "main", "isTrunk": true, "approvalStatus": "approved", … }
}

Read part of it, by line

GET/v1/agent/c/{identifier}/documents/{id}/lines

A range of lines. The other half of the outline: the outline says a section starts at line 14, and this reads it.

Query

frominteger · from 1
The first line. 1 when absent.
tointeger
The last line. 200 lines from the start when absent, so a bare call reads the first page.
draftstring
The draft to read from.
Request
curl "https://your-server.example/v1/agent/c/k7m2xq9d/documents/0d9a4c5e-…/lines?from=14&to=57" \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "id": "0d9a4c5e-7b1f-4c7a-9a10-3f6b2f1c8e21",
  "path": "field-notes/2026/harbour-survey.md",
  "title": "Harbour survey",
  "lines": [
    "## North quay",
    "",
    "The north quay was resurveyed in October…"
  ],
  "from": 14,
  "to": 57,
  "lineCount": 96,
  "hasMore": true,
  "version": { "id": "…", "seq": 41, "contentHash": "sha256:9f2c…" },
  "provenance": { "draft": "main", "isTrunk": true, "approvalStatus": "approved", … }
}

A partial read is still a read. It says whether what it returned was reviewed.

List where it is named but not linked

GET/v1/agent/c/{identifier}/documents/{id}/mentions

Places where this document's name or one of its aliases appears in plain text and is not a link. Each is a link somebody could make.

Request
curl https://your-server.example/v1/agent/c/k7m2xq9d/documents/0d9a4c5e-…/mentions \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "items": [
    {
      "documentId": "7c2f9e10-…",
      "path": "field-notes/2026/tide-tables.md",
      "title": "Tide tables",
      "term": "Harbour",
      "isAlias": true,
      "context": "…measured against the Harbour datum…",
      "line": 9
    }
  ],
  "nextCursor": null
}

List its versions

GET/v1/agent/c/{identifier}/documents/{id}/versions

Every version of a document, newest first, with who wrote it, the message they left and which lines changed. Nothing is ever overwritten: each save is a new version.

What a row tells you

lineChangeobject · null
Where the change was and how many lines it added and removed. Null on a first version and on a move.
isDeletionboolean
The version that removed the document. Read what it held with ?at=.
Request
curl "https://your-server.example/v1/agent/c/k7m2xq9d/documents/0d9a4c5e-…/versions?limit=20" \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "items": [
    {
      "id": "…",
      "seq": 41,
      "path": "field-notes/2026/harbour-survey.md",
      "title": "Harbour survey",
      "contentHash": "sha256:9f2c…",
      "byteSize": 8466,
      "wordCount": 1284,
      "lineCount": 96,
      "lineChange": { "startLine": 22, "endLine": 31, "linesAdded": 4, "linesRemoved": 2 },
      "isDeletion": false,
      "message": "Remeasure berths 3 and 4",
      "author": { "type": "USER", "id": "…", "name": "Ana Reyes", "username": "ana" },
      "createdAt": "2026-09-28T13:05:44.000Z",
      "mergedByProposal": null
    }
  ],
  "nextCursor": "41"
}

List the Concept's tags

GET/v1/agent/c/{identifier}/tags

Every tag used in a draft, most used first, with how many documents carry it. Documents the agent cannot read are not counted.

Request
curl https://your-server.example/v1/agent/c/k7m2xq9d/tags \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "items": [
    { "tag": "survey", "display": "survey", "count": 14 },
    { "tag": "tides", "display": "Tides", "count": 6 }
  ],
  "nextCursor": null
}

Writing

Every write lands on the draft named in ?draft= and makes a new version there. Each takes an optional message of up to 1,000 characters, which is what a reviewer reads beside the change.

Each answers with the document as it now is, plus lineChange, and anomalies: syntax that did not close and was read as plain text. The document is saved either way.

Write a document at a path

POST/v1/agent/c/{identifier}/documents

Writes the document at a path. If nothing is there it is created. If something is, this writes a new version of it. It is addressed by path, so it does both.

Body

pathstring · up to 1,024 · required
Relative to the Concept, with forward slashes. No leading slash and no ...
bodystring · up to 500 KB · required
The Markdown, without its properties.
frontmatterobject
The document's properties, as keys and values.
messagestring · up to 1,000
What this change does.

Refusals

  • 403direct_entry_disabledNo draft was named, so the write was aimed at the trunk. The problem's nextAction is the request that creates a draft.
  • 402plan_limitThe document is larger than the Concept's plan allows, or the Concept holds as many documents as it may.
  • 422unparseable_documentThe properties could not be read. The errors list gives the line and column.
  • 409path_reservedA recording in progress will become the document at that path.
  • 409archivedThe Concept is archived and read-only.
Request
curl -X POST \
  "https://your-server.example/v1/agent/c/k7m2xq9d/documents?draft=scribe/quay-update" \
  -H "Authorization: Bearer arb_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "path": "field-notes/2026/quay-depths.md",
    "body": "# Quay depths\n\nMeasured at low water…",
    "frontmatter": { "tags": ["survey"] },
    "message": "Add the October depth readings"
  }'
Response201
{
  "id": "e7d6c5b4-…",
  "path": "field-notes/2026/quay-depths.md",
  "title": "Quay depths",
  "body": null,
  "version": { "id": "…", "seq": 1, "contentHash": "sha256:71be…" },
  "provenance": {
    "concept": "coastal-atlas",
    "draft": "scribe/quay-update",
    "isTrunk": false,
    "approvalStatus": "unapproved",
    "proposal": null,
    "stack": null
  },
  "lineChange": null,
  "anomalies": [],
  "anomalyCount": 0,
  …
}

Replace a document

PUT/v1/agent/c/{identifier}/documents/{id}

Replaces the whole body of a document, by id. Its path stays where it is.

Body

bodystring · up to 500 KB · required
The new Markdown.
frontmatterobject
The new properties. Left as they were when absent.
expectedVersioninteger
The version.seq that was read.
messagestring · up to 1,000
What this change does.

Refusals

  • 409version_conflictThe document has changed since the version named. Read it again and decide.
  • 404not_foundNo such document on that draft.
Request
curl -X PUT \
  "https://your-server.example/v1/agent/c/k7m2xq9d/documents/0d9a4c5e-…?draft=scribe/quay-update" \
  -H "Authorization: Bearer arb_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "# Harbour survey\n\nThe north quay was resurveyed…",
    "expectedVersion": 41,
    "message": "Rewrite the north quay section"
  }'
Response200
{
  "id": "0d9a4c5e-…",
  "path": "field-notes/2026/harbour-survey.md",
  "version": { "id": "…", "seq": 42, "contentHash": "sha256:0c4d…" },
  "provenance": { "draft": "scribe/quay-update", "isTrunk": false, "approvalStatus": "unapproved", … },
  "lineChange": { "startLine": 14, "endLine": 40, "linesAdded": 9, "linesRemoved": 6 },
  "anomalies": [],
  "anomalyCount": 0,
  …
}

Edit one part of it

POST/v1/agent/c/{identifier}/documents/{id}/patch

Changes one part of a document and leaves the rest alone. The safest way to write: the agent never holds, and so never mangles, text it did not mean to touch.

Body

target.kindheading · block · frontmatter · lines · required
What is aimed at. A heading and a block take a value: the heading's text or path, or the block's name. A property takes its key. Lines take from and to.
operationreplace · append · prepend · remove · required
What to do there.
contentstring · up to 1 MB
The text, for a heading, a block or lines.
valuestring · number · boolean · null · array
The new value, for a property.
expectedVersioninteger
Optional, except on lines.
A line range is refused without expectedVersion. A heading is found again by its text and either is there or is not. A line number means something only against one version, and would otherwise overwrite whatever has moved into its place.

Refusals

  • 422not_foundNo heading, block or property of that name in the document.
  • 422validation_failedThe operation does not fit the target, or lines were aimed at without expectedVersion.
  • 409version_conflictThe document has changed since the version named.
Request
curl -X POST \
  "https://your-server.example/v1/agent/c/k7m2xq9d/documents/0d9a4c5e-…/patch?draft=scribe/quay-update" \
  -H "Authorization: Bearer arb_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "target": { "kind": "heading", "value": "Harbour survey/North quay" },
    "operation": "append",
    "content": "Berth 4 now reads 6.2 m at low water.",
    "message": "Record the berth 4 depth"
  }'
Response200
{
  "id": "0d9a4c5e-…",
  "version": { "id": "…", "seq": 43, "contentHash": "sha256:5aa1…" },
  "lineChange": { "startLine": 57, "endLine": 58, "linesAdded": 1, "linesRemoved": 0 },
  …
}

Add to the end, or the start

POST/v1/agent/c/{identifier}/documents/{id}/append

Adds text to the end of a document without sending the rest of it. /prepend adds to the start. A line break is put between the old text and the new when one is missing.

These two exist on this surface only. A person in an editor already holds the whole text. An agent adding one line to a long log should not have to carry the log there and back.

Body

contentstring · up to 1 MB · required
The text to add.
expectedVersioninteger
The version read, if it matters that nothing changed.
messagestring · up to 1,000
What this change does.
Request
curl -X POST \
  "https://your-server.example/v1/agent/c/k7m2xq9d/documents/0d9a4c5e-…/append?draft=scribe/quay-update" \
  -H "Authorization: Bearer arb_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "- 9 October: berth 4 remeasured.",
    "message": "Log the October visit"
  }'
Response200
{
  "id": "0d9a4c5e-…",
  "version": { "id": "…", "seq": 44, "contentHash": "sha256:be07…" },
  "lineChange": { "startLine": 97, "endLine": 97, "linesAdded": 1, "linesRemoved": 0 },
  …
}

POST …/prepend takes the same body and adds to the start.

Move or rename

POST/v1/agent/c/{identifier}/documents/{id}/move

Gives a document a new path. Its id, its history and its versions come with it.

A move can break the links that pointed at the old path. affected says how many, in how many documents, and the token is an offer to rewrite them. Redeem it with POST /c/{identifier}/links/rewrite before it expires.

Body

pathstring · up to 1,024 · required
The new path.
expectedVersioninteger
The version read.
messagestring · up to 1,000
Why it moved.

Refusals

  • 409path_takenA document is already at that path.
  • 409version_conflictThe document has changed since the version named.
Request
curl -X POST \
  "https://your-server.example/v1/agent/c/k7m2xq9d/documents/0d9a4c5e-…/move?draft=scribe/quay-update" \
  -H "Authorization: Bearer arb_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "path": "surveys/harbour-2026.md" }'
Response200
{
  "document": {
    "id": "0d9a4c5e-…",
    "path": "surveys/harbour-2026.md",
    "title": "Harbour survey",
    …
  },
  "affected": { "documents": 3, "links": 7 },
  "updateToken": "f1e2d3c4-b5a6-4978-8a69-7b5c4d3e2f10",
  "expiresAt": "2026-10-09T12:40:00.000Z"
}

updateToken is an offer to mend the links. It is absent when the move broke none.

Delete a document

DELETE/v1/agent/c/{identifier}/documents/{id}

Removes a document from a draft. On a draft, a removal is a change like any other. It reaches the trunk only when a proposal carrying it is reviewed and published, and until then the document is still there for everybody else.

Nothing is destroyed. Every version is kept, and the document as it stood can be read with ?at=.
Request
curl -X DELETE \
  "https://your-server.example/v1/agent/c/k7m2xq9d/documents/0d9a4c5e-…?draft=scribe/quay-update" \
  -H "Authorization: Bearer arb_live_…"
Response200
{ "ok": true }