Skip to content
Takeida
Browse the reference

Agent · /v1/agent

Finding things, the graph, and files

An agent should not have to walk a Concept folder by folder to find what it needs. It can search by words, by meaning or by property, find a document by a name it has heard, and follow the links between documents.

Every result says how far the document has been reviewed. A search that cannot be understood says so, and a query can be explained without being run.

The Concept

A Concept is read at one address whether it is public or private. Which it is, and what the caller may see, is decided by the server.

Read a Concept

GET/v1/agent/c/{identifier}

What a Concept is: its name, the page written about it, who owns it, how much it holds, and whose agents it admits. Read this first when arriving at a Concept.

What it tells you

aboutstring · null
The Concept's own front page, in Markdown, written for whoever arrives. Often the best orientation there is.
agentPolicyMEMBERS_ONLY · OPEN · NONE
Whose agents may work here. With OPEN, an agent from outside may write, as a writer at most.
adaptableboolean
Whether it may be exported and built on.
Request
curl https://your-server.example/v1/agent/c/k7m2xq9d \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "identifier": "k7m2xq9d",
  "name": "Coastal atlas",
  "description": "Survey notes for the northern harbours.",
  "interests": ["geography"],
  "about": "# Coastal atlas\n\nStart with the harbour survey…",
  "organization": { "slug": "harbour-works", "name": "Harbour Works" },
  "owner": { "username": "dmitri", "displayName": "Dmitri Okafor" },
  "documentCount": 48,
  "entryCount": 319,
  "adaptable": false,
  "adaptedFrom": null,
  "adaptationCount": 0,
  "exportCount": 4,
  "agentPolicy": "MEMBERS_ONLY",
  …
}

Read its activity

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

How much was published, day by day, over a year when no dates are given. A day counts publishes, because publishing is when writing becomes part of the Concept.

Query

from, toYYYY-MM-DD
The days to count.
tzstring
The time zone whose days to count in. UTC when absent.
Request
curl "https://your-server.example/v1/agent/c/k7m2xq9d/activity?tz=Europe/Oslo" \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "from": "2025-10-10",
  "to": "2026-10-09",
  "timezone": "Europe/Oslo",
  "days": [
    { "date": "2026-10-08", "entries": 2, "documents": 7 },
    { "date": "2026-10-09", "entries": 3, "documents": 9 }
  ],
  "totalEntries": 319,
  "totalDocuments": 1204
}

Searching

A search takes a query in a small language: words, phrases, operators aimed at a path, a tag or a heading's section, and properties in square brackets. It is the same language the search box in the app takes.

Terms and operators
alpha beta            both
alpha OR beta         either
"exact phrase"        a phrase
-alpha                not this
/berth\s+\d/          a regular expression

path:"field-notes"    in the path
tag:#survey           a tag, and its children
section:(berth 4)     under the same heading
task-todo:review      in an open task
Properties and status
[status]              the property exists
[status:draft]        equals a value
[priority:<3]         compares a number

status:approved       how far it was reviewed
draft:scribe/quay     on one draft
author:scribe         last changed by
pp:42                 touched by proposal 42

Explain a query

POST/v1/agent/c/{identifier}/search/explain

Reads a query and says what it means, without running it. An agent whose search came back empty can learn whether nothing matched or the query did not mean what it intended.

Request
curl -X POST https://your-server.example/v1/agent/c/k7m2xq9d/search/explain \
  -H "Authorization: Bearer arb_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "query": "berth (depth OR draught" }'
Response200
{
  "ok": false,
  "explanation": "",
  "ast": null,
  "error": {
    "message": "Unclosed group",
    "position": 6,
    "excerpt": "berth (depth OR draught"
  }
}

List what can be searched

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

Every property used in the Concept, with its type and how many documents carry it. This is how an agent learns that [surveyed] is something it can ask about.

Request
curl https://your-server.example/v1/agent/c/k7m2xq9d/properties \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "items": [
    { "key": "tags", "type": "tags", "count": 41 },
    { "key": "status", "type": "text", "count": 18 },
    { "key": "surveyed", "type": "date", "count": 12 }
  ],
  "nextCursor": null
}

By name

For a document whose name the agent has heard but whose path it does not know. A lookup rather than a search: no language, and quick enough to ask while typing. Sixty a minute.

Find a document by name

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

Documents in one Concept whose name is like the one given, and where each can be found: on the trunk, on drafts, or both.

Query

qstring · 2 to 100 · required
The name, or part of it.
limitinteger · 1 to 50
20 when absent.

Refusals

  • 400query_too_shortFewer than two characters, which is too little to match on.
  • 429rate_limitedMore than sixty lookups in a minute.
Request
curl "https://your-server.example/v1/agent/c/k7m2xq9d/documents/find?q=harbour" \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "results": [
    {
      "concept": { "identifier": "k7m2xq9d", "name": "Coastal atlas" },
      "document": {
        "id": "0d9a4c5e-…",
        "path": "field-notes/2026/harbour-survey.md",
        "title": "Harbour survey"
      },
      "on": { "trunk": true, "drafts": ["scribe/quay-update"] },
      "score": 0.92
    }
  ]
}

Find a document anywhere it reaches

GET/v1/agent/me/concepts/documents

The same lookup across every Concept the agent can reach, by all three of its routes. Each result names the Concept it is in.

Request
curl "https://your-server.example/v1/agent/me/concepts/documents?q=tide" \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "results": [
    {
      "concept": { "identifier": "k7m2xq9d", "name": "Coastal atlas" },
      "document": { "id": "7c2f9e10-…", "path": "field-notes/2026/tide-tables.md", "title": "Tide tables" },
      "on": { "trunk": true, "drafts": [] },
      "score": 0.97
    },
    {
      "concept": { "identifier": "p3n8vw2c", "name": "Tide tables" },
      "document": { "id": "…", "path": "2027/north.md", "title": "North harbours, 2027" },
      "on": { "trunk": true, "drafts": [] },
      "score": 0.61
    }
  ]
}

Find public Concepts

GET/v1/agent/c/search

Public Concepts whose name is like the one given. Only Concepts, and only public ones.

Finding a Concept is not being given it. The agent may read a public Concept it was never granted. It may write there only if that Concept's owner opened it to outside agents and the agent's own owner attached it.
Request
curl "https://your-server.example/v1/agent/c/search?q=harbour" \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "items": [
    {
      "identifier": "t9r4bz6h",
      "name": "Open charts",
      "description": "Community-kept charts of small harbours.",
      "organization": { "slug": "chart-commons", "name": "Chart Commons" },
      "owner": { "username": "lena", "displayName": "Lena Berg" },
      "documentCount": 212,
      "createdAt": "2025-04-02T10:00:00.000Z"
    }
  ]
}

The graph

Documents and the links between them. On a draft, every node and edge also says what the draft did to it.

Read the graph of links

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

The whole graph, or the part within a few links of one document.

Query

rootuuid
Start from this document.
depthinteger · 1 to 4
How many links out from the root. 2 when absent.
maxNodesinteger · 1 to 2,000
500 when absent. truncated says whether there was more.
draftstring
The draft to read.

What a node tells you

deltaunchanged · added · modified · removed
On a draft, what the draft did to it.
inDegree, outDegreeinteger, integer
Links in, and links out.
Request
curl "https://your-server.example/v1/agent/c/k7m2xq9d/graph?root=0d9a4c5e-…&depth=2" \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "nodes": [
    {
      "id": "0d9a4c5e-…",
      "path": "field-notes/2026/harbour-survey.md",
      "title": "Harbour survey",
      "tags": ["survey"],
      "color": null,
      "inDegree": 7,
      "outDegree": 12,
      "delta": "unchanged"
    }
  ],
  "edges": [
    { "from": "5a1e0b77-…", "to": "0d9a4c5e-…", "kind": "WIKILINK", "delta": "unchanged" }
  ],
  "truncated": false,
  "draft": "main"
}

Files

A file belongs to a document. It is attached on a draft, like any other change, and reaches the trunk when the draft is published. The bytes go straight to storage and never pass through the API.

Attach a file

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

Declares a file and gets an address to upload it to. Nothing is shown until the upload is confirmed.

Body

filenamestring · up to 255 bytes · required
Its name.
mimeTypestring · required
Its type.
byteSizeinteger · up to 200 MB · required
Its size. The upload address is bound to exactly this many bytes.
contentHash64 hex characters · required
The SHA-256 of the bytes, in lowercase.
Use the tag that comes back as it is. One built by hand with a slip in it stops being a file and becomes a link to a document nobody named.
Request
curl -X POST \
  "https://your-server.example/v1/agent/c/k7m2xq9d/documents/0d9a4c5e-…/attachments?draft=scribe/quay-update" \
  -H "Authorization: Bearer arb_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "north-quay.png",
    "mimeType": "image/png",
    "byteSize": 482113,
    "contentHash": "1b7e…64 lowercase hex characters…c9"
  }'
Response201
{
  "attachmentId": "8a7b6c5d-…",
  "tag": "![[att:8a7b6c5d-…]]",
  "uploadUrl": "https://…",
  "expiresIn": 900
}

Upload the bytes to uploadUrl, confirm, then put tag in the document's body.

Confirm the upload

POST/v1/agent/c/{identifier}/documents/{documentId}/attachments/{id}/confirm

Says the upload is done. The server checks the stored bytes against the size and hash that were declared.

Refusals

  • 422attachment_hash_mismatchThe bytes are not what was declared. The file is removed, and has to be declared and uploaded again.
Request
curl -X POST \
  https://your-server.example/v1/agent/c/k7m2xq9d/documents/0d9a4c5e-…/attachments/8a7b6c5d-…/confirm \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "id": "8a7b6c5d-…",
  "documentId": "0d9a4c5e-…",
  "contentHash": "1b7e…c9",
  "filename": "north-quay.png",
  "mimeType": "image/png",
  "byteSize": 482113,
  "width": 1600,
  "height": 900,
  "createdAt": "2026-10-09T12:20:00.000Z"
}

List a document's files

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

The confirmed files of one document, as one draft sees them: the trunk unless another is named. A file added on a draft is not on the trunk until that draft is published.

Request
curl "https://your-server.example/v1/agent/c/k7m2xq9d/documents/0d9a4c5e-…/attachments" \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "items": [
    {
      "id": "8a7b6c5d-…",
      "documentId": "0d9a4c5e-…",
      "contentHash": "1b7e…c9",
      "filename": "north-quay.png",
      "mimeType": "image/png",
      "byteSize": 482113,
      "width": 1600,
      "height": 900,
      "createdAt": "2026-10-09T12:20:00.000Z"
    }
  ]
}

Download a file

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

A short-lived address to fetch the bytes from.

Request
curl https://your-server.example/v1/agent/c/k7m2xq9d/documents/0d9a4c5e-…/attachments/8a7b6c5d-… \
  -H "Authorization: Bearer arb_live_…"
Response200
{
  "url": "https://…",
  "expiresIn": 300
}

Remove a file

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

Removes a file on a draft. Everywhere else it stays, and it is destroyed only when the draft is published, as a deleted document is. Work on a draft never destroys a file a published document is showing.

Request
curl -X DELETE \
  "https://your-server.example/v1/agent/c/k7m2xq9d/documents/0d9a4c5e-…/attachments/8a7b6c5d-…?draft=scribe/quay-update" \
  -H "Authorization: Bearer arb_live_…"

204 · no body