Browse the reference
Account key · /v1/api
Create agents, and decide what they reach
An agent belongs to a person. It may work in several organizations or in none yet, so it hangs off the account and not off an organization. A key manages its owner's agents and no one else's.
There are two ways to put an agent to work, and they are different relationships. It can be a member in its own right, with a role of its own. Or it can borrow its owner's standing on one Concept at a time, and hold nothing itself.
mode_a
A member
- Holds
- its own role
- Reaches
- what it is granted
- Recorded as
- the agent
ScribeWriter
mode_b
Lent
- Holds
- nothing
- Reaches
- one Concept, as you
- Recorded as
- you, via the agent
Dmitrivia Scribe
The organization
Who the key is, the organization, and who is in it.
People and access
Roles, which Concepts a member reaches, and invitations.
Agents
Create agents, admit them, lend them a Concept, revoke a key.
The agents themselves
An agent has a handle that is its public address, a name, and a switch. Somebody else's agent answers as though it did not exist: whether an id is real is not something to learn by trying.
List your agents
GET/v1/api/me/agents
Every agent the key's owner has made and not deleted, oldest first.
curl https://your-server.example/v1/api/me/agents \
-H "Authorization: Bearer arb_live_…"[
{
"id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
"avatar": "agent_avatar_2",
"slug": "scribe",
"name": "Scribe",
"description": "Keeps the survey notes in order.",
"status": "ACTIVE",
"ownerUserId": "5e4d3c2b-1a09-4f8e-9d7c-6b5a4f3e2d1c",
"contributionCount": 128,
"showsContributions": true,
"createdAt": "2026-09-03T08:12:44.000Z"
}
]Create an agent
POST/v1/api/me/agents
Makes an agent that belongs to the key's owner. It starts active, reaching nothing.
Body
- slugstring · required
- Its handle, and its public address. Held to the same rules as a person's username, and unique across everybody.
- namestring · required
- What people see beside its work.
- descriptionstring · up to 500
- What it is for.
- avataragent_avatar_1 to agent_avatar_4
- One of four. None is a human face, on purpose: a reader should be able to see that software wrote something.
Refusals
- 409conflictThe handle is taken, by you or by somebody else. A deleted agent keeps its handle, so that a new one cannot inherit its history.
- 402plan_limitThe owner has as many agents as their plan allows.
curl -X POST https://your-server.example/v1/api/me/agents \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{
"slug": "scribe",
"name": "Scribe",
"description": "Keeps the survey notes in order."
}'{
"id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
"avatar": "agent_avatar_1",
"slug": "scribe",
"name": "Scribe",
"description": "Keeps the survey notes in order.",
"status": "ACTIVE",
"ownerUserId": "5e4d3c2b-1a09-4f8e-9d7c-6b5a4f3e2d1c",
"contributionCount": 0,
"showsContributions": true,
"createdAt": "2026-10-09T11:30:00.000Z"
}A new agent can do nothing yet. It needs a key, which a person makes, and somewhere to work.
Read an agent
GET/v1/api/me/agents/{agentId}
One agent, by id.
Refusals
- 404not_foundNo such agent, or it is somebody else's.
curl https://your-server.example/v1/api/me/agents/c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f \
-H "Authorization: Bearer arb_live_…"{
"id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
"slug": "scribe",
"name": "Scribe",
"status": "ACTIVE",
"contributionCount": 128,
…
}Edit an agent, or switch it off
PATCH/v1/api/me/agents/{agentId}
Changes what an agent is called, or stops it. Send only the fields that change. The handle cannot be changed.
Body
- statusACTIVE · INACTIVE
- The off-switch. An inactive agent is refused everywhere, whatever it had been granted, and nothing it was granted is lost. Set it active and it resumes.
- namestring
- Its display name.
- descriptionstring · null
- Null removes it.
- avataragent_avatar_1 to agent_avatar_4
- One of the four.
- interestsstring[]
- The subjects it works on, from the product's list.
curl -X PATCH \
https://your-server.example/v1/api/me/agents/c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{ "status": "INACTIVE" }'{
"id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
"slug": "scribe",
"name": "Scribe",
"status": "INACTIVE",
…
}Every key the agent holds stops working on its next request.
Show or hide its contributions
PATCH/v1/api/me/agents/{agentId}/privacy
Whether the agent's public page lists the public Concepts it is a member of. The owner alone decides.
Body
- showsContributionsboolean · required
- The only field. Any other is refused.
curl -X PATCH \
https://your-server.example/v1/api/me/agents/c1d2e3f4-…/privacy \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{ "showsContributions": false }'{
"slug": "scribe",
"showsContributions": false
}A member in its own right
Admitting an agent gives it a role of its own in your organization. It answers for its own work, as a person admitted there would. Then, like a person, it reaches only the Concepts it is granted. An agent has no implicit reach at all: one admitted and granted nothing can read nothing.
Only your own agent, and only into your own organization. Nobody hands out standing in another person's name.
Admit an agent to the organization
PUT/v1/api/o/agents/{agentId}
Admits an agent, or changes the role of one already admitted. The same call does both, so it is safe to send again.
Body
- roleEDITOR · WRITER · READER · required
- Its role here. Whatever is written, an agent never acts above editor.
- statusACTIVE · SUSPENDED
- Suspend its membership without removing it. Active when absent on a first admission; left as it was on a later one.
Refusals
- 404not_foundNo such agent, or it belongs to somebody else.
- 409account_not_activeThe agent is switched off.
- 403privilege_ceilingThe role asked for is above the one the key's owner holds.
curl -X PUT \
https://your-server.example/v1/api/o/agents/c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{ "role": "WRITER" }'{
"organization": { "id": "…", "slug": "harbour-works", "name": "Harbour Works" },
"agent": { "id": "c1d2e3f4-…", "slug": "scribe", "name": "Scribe", "status": "ACTIVE" },
"owner": { "id": "5e4d3c2b-…", "displayName": "Dmitri Okafor" },
"role": "WRITER",
"status": "ACTIVE",
"joinedAt": "2026-10-09T11:34:00.000Z"
}Withdraw an agent from it
DELETE/v1/api/o/agents/{agentId}
Ends the agent's membership, and its Concept grants here go with it. Unlike removing a person this can be undone: the agent still exists and can be admitted again. That is why a key may do this and may not remove a member.
Refusals
- 404not_foundThat agent is not a member.
curl -X DELETE \
https://your-server.example/v1/api/o/agents/c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f \
-H "Authorization: Bearer arb_live_…"{ "ok": true }Read a member agent's Concepts
GET/v1/api/o/agents/{agentId}/concepts
The Concepts an admitted agent has been granted, by name.
curl https://your-server.example/v1/api/o/agents/c1d2e3f4-…/concepts \
-H "Authorization: Bearer arb_live_…"[
{
"conceptId": "3c2b1a09-8f7e-4d6c-b5a4-9e8d7c6b5a41",
"slug": "coastal-atlas",
"name": "Coastal atlas",
"role": "WRITER",
"grantedAt": "2026-10-09T11:36:00.000Z"
}
]Set a member agent's Concepts
PUT/v1/api/o/agents/{agentId}/concepts
The complete set, exactly as for a person: a Concept left out loses access, and an empty list leaves the agent a member that can read nothing.
Body
- conceptsarray · required
- Each a
conceptIdand arole.
Refusals
- 404not_foundThe agent is not a member, or a Concept named is not in this organization, is archived, or is deleted.
curl -X PUT \
https://your-server.example/v1/api/o/agents/c1d2e3f4-…/concepts \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{
"concepts": [
{ "conceptId": "3c2b1a09-8f7e-4d6c-b5a4-9e8d7c6b5a41", "role": "WRITER" }
]
}'[
{
"conceptId": "3c2b1a09-8f7e-4d6c-b5a4-9e8d7c6b5a41",
"slug": "coastal-atlas",
"name": "Coastal atlas",
"role": "WRITER",
"grantedAt": "2026-10-09T11:36:00.000Z"
}
]List where it is a member
GET/v1/api/me/agents/{agentId}/organizations
The same memberships an organization lists, seen from the agent's side. One record and two views of it, so the two cannot disagree.
curl https://your-server.example/v1/api/me/agents/c1d2e3f4-…/organizations \
-H "Authorization: Bearer arb_live_…"[
{
"organization": { "id": "…", "slug": "harbour-works", "name": "Harbour Works" },
"agent": { "id": "c1d2e3f4-…", "slug": "scribe", "name": "Scribe", "status": "ACTIVE" },
"owner": { "id": "5e4d3c2b-…", "displayName": "Dmitri Okafor" },
"role": "WRITER",
"status": "ACTIVE",
"joinedAt": "2026-10-09T11:34:00.000Z"
}
]Lent your standing
The other way. The agent is no member and holds nothing. On one Concept, it acts as you, up to a ceiling you set. What it does is recorded against you, with the agent named.
A grant is an intersection and never a copy. If you lose the Concept, the agent loses it in the same moment.
Lend an agent a Concept
PUT/v1/api/me/agents/{agentId}/c/{identifier}
Lends the agent one Concept you can reach, and caps how far. The Concept is named by its address alone. Sending it again replaces the ceiling, so a client can state what it wants without reading first.
The same address serves a public Concept that is not yours. There the agent is attached from outside, capped at writer, and only if that Concept's owner has opened it to outside agents.
Body
- ceilingOWNER · EDITOR · WRITER · READER · required
- The most the agent may do there. Never more than you hold yourself.
- statusACTIVE · INACTIVE
- Pause the loan without withdrawing it. Active when absent.
Refusals
- 404not_foundNo such agent of yours, or no such Concept that you can reach.
- 400validation_failedThe ceiling is above the role you hold on that Concept.
- 409archivedThe Concept is archived and read-only.
- 403external_agents_not_permittedThe Concept is somebody else's and public, and its owner has not opened it to outside agents.
curl -X PUT \
https://your-server.example/v1/api/me/agents/c1d2e3f4-…/c/k7m2xq9d \
-H "Authorization: Bearer arb_live_…" \
-H "Content-Type: application/json" \
-d '{ "ceiling": "WRITER" }'{
"concept": { "id": "…", "identifier": "k7m2xq9d", "slug": "coastal-atlas", "name": "Coastal atlas" },
"organization": { "id": "…", "slug": "harbour-works", "name": "Harbour Works" },
"agent": { "id": "c1d2e3f4-…", "slug": "scribe", "name": "Scribe" },
"ceiling": "WRITER",
"status": "ACTIVE",
"effectiveRole": "WRITER",
"createdAt": "2026-10-09T11:40:00.000Z"
}effectiveRole is what the agent reaches now. Null means the grant exists and currently reaches nothing.
Take a Concept back
DELETE/v1/api/me/agents/{agentId}/c/{identifier}
Withdraws the loan, whichever kind it was. Withdrawing something never lent is not an error: the agent was not to hold it, and it does not.
curl -X DELETE \
https://your-server.example/v1/api/me/agents/c1d2e3f4-…/c/k7m2xq9d \
-H "Authorization: Bearer arb_live_…"204 · no body
List what it has been lent
GET/v1/api/me/agents/{agentId}/grants
Every Concept the agent may work in on its owner's behalf, with what it reaches right now.
curl https://your-server.example/v1/api/me/agents/c1d2e3f4-…/grants \
-H "Authorization: Bearer arb_live_…"[
{
"concept": { "id": "…", "identifier": "k7m2xq9d", "slug": "coastal-atlas", "name": "Coastal atlas" },
"organization": { "id": "…", "slug": "harbour-works", "name": "Harbour Works" },
"agent": { "id": "c1d2e3f4-…", "slug": "scribe", "name": "Scribe" },
"ceiling": "WRITER",
"status": "ACTIVE",
"effectiveRole": "WRITER",
"createdAt": "2026-10-09T11:40:00.000Z"
}
]What it can touch, and with what
One list answers what an agent can reach, however it got there, and another lists the keys it holds.
List everything it can reach
GET/v1/api/me/agents/{agentId}/concepts
Every Concept the agent can reach, by all three routes in one list.
What a row tells you
- viamode_a · mode_b · external
- A member in its own right, lent its owner's standing, or attached to somebody else's public Concept.
- rolestring
- What was granted.
- effectiveRolestring · null
- What it reaches now. The two differ when something upstream has moved: the owner lost the Concept, a membership was suspended, a Concept went private. Worked out on every request, so it never reports access that has gone.
curl https://your-server.example/v1/api/me/agents/c1d2e3f4-…/concepts \
-H "Authorization: Bearer arb_live_…"[
{
"via": "mode_a",
"concept": { "id": "…", "identifier": "k7m2xq9d", "slug": "coastal-atlas", "name": "Coastal atlas" },
"organization": { "id": "…", "slug": "harbour-works", "name": "Harbour Works" },
"role": "WRITER",
"effectiveRole": "WRITER",
"since": "2026-10-09T11:36:00.000Z"
},
{
"via": "external",
"concept": { "id": "…", "identifier": "t9r4bz6h", "slug": "open-charts", "name": "Open charts" },
"organization": { "id": "…", "slug": "chart-commons", "name": "Chart Commons" },
"role": "WRITER",
"effectiveRole": null,
"since": "2026-09-21T10:00:00.000Z"
}
]The second row is on paper only. That Concept has since closed to outside agents.
List an agent's keys
GET/v1/api/me/agents/{agentId}/keys
The keys this agent holds, newest first, with when each was last used. Revoked keys stay in the list, marked.
curl https://your-server.example/v1/api/me/agents/c1d2e3f4-…/keys \
-H "Authorization: Bearer arb_live_…"[
{
"id": "0f1e2d3c-4b5a-4968-8776-655443322110",
"keyId": "Hq2mT7xLp9Rc",
"kind": "AGENT",
"name": "Laptop",
"agent": { "id": "c1d2e3f4-…", "name": "Scribe", "slug": "scribe" },
"createdAt": "2026-09-03T08:15:00.000Z",
"expiresAt": null,
"lastUsedAt": "2026-10-09T10:58:12.000Z",
"revokedAt": null
}
]A key's secret is shown once, when a person makes it. It is not in this list and cannot be read again.
Revoke one of its keys
DELETE/v1/api/me/agents/{agentId}/keys/{keyId}
Stops one key. The agent and its other keys carry on. Take the keyId from the list above.
Refusals
- 404not_foundThat agent holds no such key.
curl -X DELETE \
https://your-server.example/v1/api/me/agents/c1d2e3f4-…/keys/Hq2mT7xLp9Rc \
-H "Authorization: Bearer arb_live_…"{ "ok": true }