Operation reference

Look up API actions, their arguments, defaults and result types.

On this page

Find the API action you need by resource below. Each entry gives its name, arguments and result type. These declarations are generated from Cabane's API; Requests and responses explains the request format and error handling.

To call an entry marked // query, send a POST to https://app.cabane.ai/api/workspaces/{workspaceId}/ops/query. For // mutate, use the same URL ending in /mutate. Put the entry's name in op and its named arguments in args. For example, files.read becomes:

json
{"op": "files.read", "args": {"path": "/reports/weekly.md"}}

Send a bearer token and Content-Type: application/json; First API request has a complete curl example.

In the declarations, ? marks an optional argument, = gives its default, and id|reference means either named argument is accepted. The type after → describes the successful result. Brackets state limits and conditions, such as [requires overwrite=true].

For the full definitions of an entry's input and result types, send its name to /ops/describe:

json
{"op": "files.read"}

The response includes the declaration and the types it refers to. Sending {} lists the available operations. This is also useful when your code needs to discover the API on a particular Cabane server.

agents

agents.activate

Turn a deactivated agent back on — a reversible off/on that isn't deletion (a deactivated agent keeps their config + handle but is skipped by dispatch).

// mutate
agents.activate(id|handle:string) → AgentLifecycleReceipt

agents.create

Create an in-app agent.

// mutate
agents.create(
  name:string,
  handle:string,
  charter?:string|null
) → AgentLifecycleReceipt

agents.deactivate

Deactivate an agent — a reversible off (config + handle kept; skipped by dispatch).

// mutate
agents.deactivate(id|handle:string) → AgentLifecycleReceipt

agents.delete

Delete (soft-archive/revoke) an agent — IRREVERSIBLE (there's no un-delete; create a fresh agent instead).

// mutate
agents.delete(id|handle:string) → AgentDeleteReceipt

agents.list

The workspace's agents (your peers).

// query
agents.list(
  cursor?:string,
  maxBytes?:number=16 KiB,
  includeInactive?:boolean=false
) → {agents,more}

agents.read

Read one agent by id or handle, whichever you hold, including public details and their look.

// query
agents.read(id|handle:string) → AgentReadResult

agents.update

The one configure verb — set name, handle, charter, and run-config in one partial, atomic write.

// mutate
agents.update(
  id|handle:string,
  name?:string,
  newHandle?:string,
  charter?:string|null,
  runConfig?:Record<string,unknown>,
  color?:number,
  shape?:'round'|'square'|'drop'|'hex',
  topper?:'none'|'sprout'|'antenna',
  identityEditing?:'members'|'owner',
  roomPlacement?:'members'|'owner'
) → AgentLifecycleReceipt & { clearedConversationCount: number }

asks

asks.list

Asks across the workspace, newest first, filtered before paging.

// query
asks.list(
  roomId|roomSlug?:string,
  createdAfter?:string,
  createdBefore?:string,
  resolvedAfter?:string[requires state!=open],
  resolvedBefore?:string[requires state!=open],
  target?:string,
  asker?:string,
  state?:AskState|AskState[]=open,
  conversationId?:string,
  order?:'asc'|'desc'=desc,
  limit?:number=50,
  cursor?:string,
  maxBytes?:number=16 KiB
) → {asks,more}

boards

boards.create

Create a workroom's board, with a required workspace-unique prefix of 1–12 letters.

// mutate
boards.create(
  name:string,
  prefix:string,
  roomId:string,
  description?:string
) → { board: TaskBoardSummary }

boards.delete

Delete the board and every task on it, each as tasks.delete does.

// mutate
boards.delete(
  id|slug|prefix:string,
  ifVersion:number
) → { deleted: true; board: TaskBoardSummary }

boards.list

Workspace boards, each belonging to one workroom, at most one per room.

// query
boards.list(
  limit?:number=100,
  cursor?:string,
  maxBytes?:number=16 KiB,
  includeArchived?:boolean=false
) → {boards,more}

boards.read

Board metadata by id, slug or prefix, whichever you hold — an object, never JSON text.

// query
boards.read(
  id|slug|prefix:string,
  maxBytes?:number=16 KiB,
  part?:string,
  startLine?:number,
  startOffset?:number,
  ifVersion?:number,
  ifSha?:string
) → TaskBoardReadResult{more,…}

boards.update

Rename a board, edit its description, or set the template a task created on it takes by default — never renumbering its tasks.

// mutate
boards.update(
  id|slug|prefix:string,
  ifVersion:number,
  name?:string,
  description?:string,
  defaultTemplate?:string|null
) → { board: TaskBoardSummary }

browser

browser.close

Close this conversation's shared browser when the job is done and nobody is watching.

// mutate
browser.close() → BrowserStatus

browser.open

Open this conversation's shared browser, or return the one already open (created: false).

// mutate
browser.open(fresh?:boolean=false) → BrowserOpenResult

browser.status

Where this conversation's shared browser stands: available in the workspace, attached to your browser server this turn, and the live session if there is one.

// query
browser.status() → BrowserStatus

context

context.around

Orient around a thing you've found — it PLUS its immediate connected context (the docs where the thinking lives, the threads it spawned, who's involved).

// query
context.around(
  ref:string,
  kind?:GraphNodeKind,
  maxBytes?:number=16 KiB
) → ContextAroundResult

context.conversationsSince

The CONVERSATIONS that moved in a time range (the moved list), plus the files each touched and who took part — each moved conversation's own participants, exactly as on around.

// query
context.conversationsSince(
  from:string,
  until?:string,
  roomId|roomSlug?:string,
  involving?:string,
  limit?:number=20,
  maxBytes?:number=16 KiB,
  cursor?:string
) → ContextSinceResult{moved,more,…}

context.fileChanges

Recent FILE/FOLDER change events (the compact projection), newest-first — who touched what.

// query
context.fileChanges(
  from?:string,
  until?:string,
  under?:string,
  types?:string[],
  actor?:string,
  limit?:number=25,
  maxBytes?:number=16 KiB,
  cursor?:string,
  order?:'asc'|'desc'=desc
) → {changes,more}

context.references

The BATCHED edge primitive: "what is each of these objects connected to, and when did each reference form?" One hop of graph neighbours of things you already know — the context-cluster sibling of around (which carries the shaped seed).

// query
context.references(
  ref:string,
  limit?:number=20,
  order?:'asc'|'desc'=desc,
  edges?:string[],
  to?:string[],
  direction?:'out'|'in'|'any'=any,
  since?:string,
  preview?:number=0,
  maxBytes?:number=16 KiB,
  cursor?:string
) → ReferencesResult{references,more,…}

context.referencesMany

The batch form of references: up to 25 refs (paths and conversation ids mixable) → { results, unresolved, truncated }, one result per ref with truncated saying its per-source limit filled.

// query
context.referencesMany(
  refs:string[],
  limit?:number=20,
  order?:'asc'|'desc'=desc,
  edges?:string[],
  to?:string[],
  direction?:'out'|'in'|'any'=any,
  since?:string,
  preview?:number=0
) → ReferencesBatchResult{results,unresolved,…}

conversations

conversations.create

Open a conversation in the required room (roomId or roomSlug), which must be a WORKROOM.

// mutate
conversations.create(
  body:string,
  roomId|roomSlug:string,
  modifiedEntries?:{ kind: 'file'|'folder'; id: string }[],
  title?:string[≤72],
  idempotencyKey?:string,
  agents?:ConversationSeatInput[]
) → { conversation: Conversation; message: PostedMessageRow; replayed?: true; unappliedSeats?: Array<{ agentId: string; reason: 'agent_unavailable' | 'execution_config_stale' | 'execution_locked' }> } & AddressedReport

conversations.delete

The one HARD delete — drops the conversation row and every message in the thread (cascade).

// mutate
conversations.delete(
  id:string
) → ConversationDeleteManifest{cancelFailures,…}

conversations.follow

Make people FOLLOW a conversation — quietly.

// mutate
conversations.follow(
  id:string,
  followers:string|string[]
) → ConversationParticipants{roomId,followers,agents,…}

conversations.list

The one list verb, and it FILTERS NOTHING by default: a bare page is every conversation in the workspace — scheduled runs included, one flat page ordered by activity.

// query
conversations.list(
  roomId|roomSlug?:string,
  contains?:string,
  participant?:string,
  involving?:string,
  ids?:string[],
  updatedAfter?:string,
  updatedBefore?:string,
  createdAfter?:string,
  createdBefore?:string,
  relevantTo?:string,
  scheduled?:boolean,
  scheduleId?:string,
  taskId|taskReference?:string,
  working?:boolean,
  queued?:boolean,
  finished?:boolean,
  sort?:'activity'|'created'=activity,
  total?:boolean=false,
  summary?:boolean=true,
  cursor?:string,
  limit?:number=25,
  maxBytes?:number=16 KiB,
  order?:'asc'|'desc'=desc
) → {conversations,more,…}

conversations.listMessages

A page of the conversation as a person reads it.

// query
conversations.listMessages(
  id:string,
  include?:'narrative'|'all'=narrative,
  turnId?:string,
  author?:string,
  mentions?:string,
  landedAfter?:string,
  landedBefore?:string,
  limit?:number=20,
  order?:'asc'|'desc'=desc,
  maxBytes?:number=16 KiB,
  cursor?:string,
  afterId?:string[excludes cursor,order=desc],
  ids?:string[][excludes limit,cursor,order,afterId,include,turnId,author,mentions,landedAfter,landedBefore],
  edges?:boolean=false
) → {messages,more}

conversations.participants

Who follows THIS thread, and which agents have worked in it.

// query
conversations.participants(
  id:string
) → ConversationParticipants{roomId,followers,agents,…}

conversations.post

Post into an existing conversation.

// mutate
conversations.post(
  id:string,
  body:string,
  modifiedEntries?:{ kind: 'file'|'folder'; id: string }[],
  idempotencyKey?:string
) → { message: PostedMessageRow; replayed?: true } & AddressedReport

conversations.read

One conversation's row.

// query
conversations.read(id:string) → { conversation: Conversation }

conversations.readMessage

One message, whole — the read a row's more points at, and the only place an ask is expanded in full: every headline, every answer, the addendum, through whichever message you name (the one that poses the ask or the one that resolves …

// query
conversations.readMessage(
  id:string,
  startLine?:number=1,
  endLine?:number,
  maxBytes?:number=16 KiB,
  startOffset?:number,
  ifSha?:string,
  part?:'body'|'addendum'|`answer:${string}`=body
) → MessageRead{id,authorUserId,agentId,turnId,more,…}

conversations.unfollow

The paired verb.

// mutate
conversations.unfollow(
  id:string,
  followers:string|string[]
) → ConversationParticipants{roomId,followers,agents,…}

conversations.update

Retitle a conversation, or write its summary.

// mutate
conversations.update(
  id:string,
  title?:string|null[≤72],
  summary?:string|null[≤2400],
  ifSummaryVersion?:number
) → { conversation: Conversation }

devices

devices.list

Every device brought to this workspace, the same list for every member and agent here: its owner, availability and state, each harness's state, companion version, lastSeenAt, placement, and which of this workspace's agents run on…

// query
devices.list(cursor?:string, maxBytes?:number=16 KiB) → {devices,more}

devices.read

One device brought to this workspace, by its id (from devices.list, or an agent's executor.deviceId on agents.read), with the agents assigned to it, ordered by id.

// query
devices.read(
  id:string,
  cursor?:string,
  maxBytes?:number=16 KiB
) → {more,…}

docs

docs.list

Cabane's user guides, in reading order: slug, title, summary, last-updated day and public URL.

// query
docs.list(cursor?:string, maxBytes?:number=16 KiB) → {guides,more}

docs.read

One user guide as Markdown, the same text as its public page.

// query
docs.read(
  slug:string,
  startLine?:number=1,
  endLine?:number,
  maxBytes?:number=16 KiB,
  startOffset?:number,
  ifSha?:string
) → DocsGuideContent{more,…}

files

files.archive

Take something out of the way WITHOUT moving it — the row is stamped archived at the path it already had, so every link into it still opens.

// mutate
files.archive(path:string) → StateManifest{path,entries,…}

files.copy

Copy without streaming bytes.

// mutate
files.copy(
  from:string,
  to:string,
  recursive?:boolean=false,
  onConflict?:'error'|'rename'=error
) → FileCopyResult{sha256,…}

files.delete

Soft-delete.

// mutate
files.delete(
  path:string,
  recursive?:boolean=false
) → DeleteManifest{path,entries,…}

files.edit

Modify a text file in place, atomically.

// mutate
files.edit(
  path:string,
  edits:FileEditInput|FileEditInput[],
  ifSha?:string
) → FileEditResult{path,sha256,…}

files.import

The BYTE path (write is text-only): the SERVER fetches an https url into the workspace at path — no bytes ride through model context.

// mutate
files.import(
  url:string,
  path:string,
  overwrite?:boolean=false,
  mkdirs?:boolean=true,
  mime?:string
) → FileWriteResult{path,sha256,…}

files.list

List a folder (omit path for the root /).

// query
files.list(
  path?:string,
  mime?:string,
  contains?:string,
  updatedAfter?:string,
  updatedBefore?:string,
  recursive?:boolean=false,
  maxBytes?:number=16 KiB
) → FolderListing{path,folders,files,…}

files.listArchive

What is ARCHIVED — out of the way at the path it always had, not moved anywhere.

// query
files.listArchive(
  from?:string,
  until?:string,
  under?:string,
  rootsOnly?:boolean=false,
  limit?:number=50,
  maxBytes?:number=16 KiB,
  cursor?:string,
  order?:'asc'|'desc'=desc
) → {entries,more}

files.listTrash

The soft-deleted entries (files + folders), most recently deleted first — pass one's path to restore.

// query
files.listTrash(
  from?:string,
  until?:string,
  under?:string,
  rootsOnly?:boolean=false,
  limit?:number=50,
  maxBytes?:number=16 KiB,
  cursor?:string,
  order?:'asc'|'desc'=desc
) → {entries,more}

files.listVersions

The file's append-only version history, newest-first (order: 'asc' for v1-up), paginated.

// query
files.listVersions(
  path:string,
  limit?:number=50,
  maxBytes?:number=16 KiB,
  cursor?:string,
  order?:'asc'|'desc'=desc
) → FileVersionsResult{path,fileId,versions,more,…}

files.mkdir

Create a folder.

// mutate
files.mkdir(path:string, recursive?:boolean=false) → {path,…}

files.move

Move or rename.

// mutate
files.move(
  from:string,
  to:string
) → { from: string; to: string; kind: 'file' | 'folder'; invalidated: MoveInvalidation }

files.read

Read one file.

// query
files.read(
  path:string,
  startLine?:number=1,
  endLine?:number,
  maxBytes?:number=16 KiB,
  startOffset?:number,
  ifSha?:string
) → FileRead{path,sha256,more,…}

files.readMany

MANY files under a glob, in one call, projected to what you need — { entries, more }, always a collection.

// query
files.readMany(
  pattern:string,
  include?:'properties'|'content'|'both'=properties,
  includeArchived?:boolean=false,
  maxBytes?:number=16 KiB,
  cursor?:string
) → ReadManyResult{entries,more,…}

files.readVersion

The bytes of one older version (versionId from a listVersions row).

// query
files.readVersion(
  path:string,
  versionId:string,
  startLine?:number=1,
  endLine?:number,
  maxBytes?:number=16 KiB,
  startOffset?:number,
  ifSha?:string
) → VersionContentResult{path,versionId,more,…}

files.restore

Lift the tombstone on a soft-deleted entry, addressed by the path it had when deleted (match a listTrash() entry).

// mutate
files.restore(path:string) → RestoreManifest{path,entries,…}

files.restoreVersion

Restore an earlier version's bytes onto the file — the recovery half of the history surface.

// mutate
files.restoreVersion(
  path:string,
  versionId:string
) → RestoreVersionResult{path,sha256,…}

files.setProperties

Set a document's properties (its leading --- block) as data instead of find-and-replacing text.

// mutate
files.setProperties(
  path:string,
  properties:Record<string,unknown>,
  ifSha?:string
) → FileEditResult{path,sha256,…}

files.stat

A cheap existence/metadata probe that NEVER throws on a missing path — it returns { exists: false }.

// query
files.stat(path:string) → FileStat{sha256,…}

files.unarchive

Bring an archived entry back to the live tree, addressed by its path.

// mutate
files.unarchive(path:string) → StateManifest{path,entries,…}

files.write

Create a file.

// mutate
files.write(
  path:string,
  content:string,
  overwrite?:boolean=false,
  mkdirs?:boolean=true,
  mime?:string,
  ifSha?:string[requires overwrite=true],
  properties?:'keep'|object[excludes dangerouslyClearProperties],
  dangerouslyClearProperties?:boolean=false
) → FileWriteResult{path,sha256,…}

find

find.paths

Names/paths by glob (anchored at root; ** crosses segments) — reads no bodies, cheap.

// query
find.paths(
  pattern:string,
  maxBytes?:number=16 KiB,
  cursor?:string,
  includeArchived?:boolean=false
) → PathsResult{paths,more,…}

find.scan

Every matching LINE under a subtree — a bounded WORKSPACE SCAN that reads file bodies (the EXPENSIVE discovery verb).

// query
find.scan(
  pattern:string,
  under?:string,
  includeArchived?:boolean=false,
  ignoreCase?:boolean=false,
  fixed?:boolean=false,
  maxBytes?:number=16 KiB,
  cursor?:string
) → ScanResult{matches,more,…}

find.search

Ranked, indexed, CHEAP discovery across file names/content AND conversation titles/messages — the default first move when you don't know where a thing lives.

// query
find.search(
  q:string,
  tiers?:Array<'named'|'distilled'|'raw'>,
  from?:{ conversationId?: string; roomId?: string; roomSlug?: string },
  explain?:boolean=false,
  matchOn?:'name'|'content'|'all'=all,
  types?:Array<'files'|'conversations'|'tasks'>,
  conversationId?:string[excludes roomId,roomSlug,under],
  author?:string,
  mentions?:string,
  landedAfter?:string,
  landedBefore?:string,
  conversationMode?:'grouped'|'messages'=grouped,
  roomId|roomSlug?:string,
  under?:string,
  limit?:number=30,
  maxBytes?:number=16 KiB,
  cursor?:string,
  includeArchived?:boolean=false
) → SearchResult{tiers,results,more,…}

people

people.list

The workspace's people (owner first), each { id, membershipId, name, handle, avatarUrl, email, role }.

// query
people.list(cursor?:string, maxBytes?:number=16 KiB) → {members,more}

rooms

rooms.archive

Soft-archive a room — which CLOSES it (CT1639).

// mutate
rooms.archive(id|slug:string) → { room: RoomRow }

rooms.create

Create a PUBLIC room and its /rooms/{slug} reserved folder (with its ABOUT.md charter).

// mutate
rooms.create(
  name:string[≤80],
  type?:'chatroom'|'workroom'=workroom,
  charter?:string,
  people?:string|string[],
  defaultAgent?:string|null,
  visibility?:'public'=public
) → { room: RoomRow }

rooms.join

Put people in the room — quietly.

// mutate
rooms.join(
  id|slug:string,
  people:string|string[]
) → RoomReadResult{people,agents,pins,…}

rooms.leave

Take people out of the room — the paired verb.

// mutate
rooms.leave(
  id|slug:string,
  people:string|string[]
) → RoomReadResult{people,agents,pins,…}

rooms.list

The workspace's rooms.

// query
rooms.list(
  cursor?:string,
  maxBytes?:number=16 KiB,
  includeArchived?:boolean=false,
  member?:string,
  type?:'chatroom'|'workroom'
) → {rooms,more}

rooms.pin

Pin one of the room's OWN files to it — the shortlist under the room header's Files chevron, where everyone in the room sees it.

// mutate
rooms.pin(
  id|slug:string,
  path:string
) → RoomReadResult{people,agents,pins,…}

rooms.read

One room whole: its row, the path of its charter (ABOUT.md), the people who joined it and the agents in it.

// query
rooms.read(id|slug:string) → RoomReadResult{people,agents,pins,…}

rooms.repairFolder

Repair legacy record/folder desync by moving the named reserved room folder to the record's canonical homeFolder.

// mutate
rooms.repairFolder(
  id|slug:string,
  fromPath:string,
  replaceGeneratedHome?:boolean=false
) → RoomFolderRepairReceipt

rooms.unarchive

Restore a soft-archived room to the sidebar, its reserved folder with it — and reopen it: posting, opening conversations and waking work again (CT1639).

// mutate
rooms.unarchive(id|slug:string) → { room: RoomRow }

rooms.unpin

Unpin a file from the room.

// mutate
rooms.unpin(
  id|slug:string,
  path:string
) → RoomReadResult{people,agents,pins,…}

rooms.update

The one configure verb — change a room's name, description or seated agent in one partial write.

// mutate
rooms.update(
  id|slug:string,
  name?:string[≤80],
  description?:string|null[≤1000],
  defaultAgent?:string|null,
  owner?:string
) → { room: RoomRow }

schedules

schedules.cancel

Cancel a recurring schedule — a terminal stop (use pause for a reversible hold).

// mutate
schedules.cancel(id:string) → { schedule: ScheduleRow }

schedules.create

Create a recurring schedule that dispatches agent on the cron cadence, into exactly one target: targetRoomId, where each fire opens a new conversation, or targetConversationId, where each fire posts a message into that existing conve…

// mutate
schedules.create(
  agent:string,
  title:string,
  prompt:string[≤16KiB],
  targetRoomId|targetRoomSlug|targetConversationId:string,
  cron:string,
  tz:string,
  principal?:'creator'|'workspace'=creator
) → { schedule: ScheduleRow }

schedules.list

The workspace's live RECURRING schedules (active/paused), soonest-created first.

// query
schedules.list(
  agent?:string,
  state?:ScheduleState|ScheduleState[],
  targetRoomId|targetRoomSlug?:string,
  targetConversationId?:string,
  limit?:number=25,
  maxBytes?:number=16 KiB,
  cursor?:string,
  order?:'asc'|'desc'=desc
) → {schedules,more}

schedules.pause

Pause a recurring schedule — stops firing until resumed (missed fires are skipped, never backfilled).

// mutate
schedules.pause(id:string) → { schedule: ScheduleRow }

schedules.read

One schedule whole — either kind, any state — including the standing prompt a list row only excerpts.

// query
schedules.read(id:string) → { schedule: ScheduleView }

schedules.resume

Resume a paused recurring schedule — its next run is recomputed from now.

// mutate
schedules.resume(id:string) → { schedule: ScheduleRow }

schedules.runNow

Fire a schedule ONCE, right now — the way to find out whether a schedule works without waiting for its cadence.

// mutate
schedules.runNow(
  id:string
) → { schedule: ScheduleRow } & ConversationLabels

schedules.update

Patch a recurring schedule — any of title / prompt / recurrence (pass BOTH cron and tz) / targetRoomId or targetConversationId / agent / principal.

// mutate
schedules.update(
  id:string,
  title?:string,
  prompt?:string[≤16KiB],
  cron?:string[requires tz],
  tz?:string[requires cron],
  targetRoomId|targetRoomSlug|targetConversationId?:string,
  agent?:string,
  principal?:'creator'|'workspace'
) → { schedule: ScheduleRow }

skills

skills.list

Every skill reachable this turn, Cabane-origin first, then the turn's room's, then the workspace's.

// query
skills.list(cursor?:string, maxBytes?:number=16 KiB) → {skills,more}

skills.read

One skill by name — the file as authored, frontmatter included.

// query
skills.read(
  name:string,
  startLine?:number=1,
  endLine?:number,
  maxBytes?:number=16 KiB,
  startOffset?:number,
  ifSha?:string
) → SkillContent{path,roomId,more,…}

tags

tags.create

Create a trimmed, case-insensitive unique tag.

// mutate
tags.create(name:string) → { tag: TaskTag }

tags.delete

Remove an unused tag.

// mutate
tags.delete(
  id:string,
  ifVersion:number
) → { deleted: true; tag: TaskTag }

tags.list

Workspace-wide tags, paged.

// query
tags.list(
  limit?:number=100,
  cursor?:string,
  maxBytes?:number=16 KiB
) → {tags,more}

tags.update

Rename a tag everywhere without changing its identity.

// mutate
tags.update(id:string, name:string, ifVersion:number) → { tag: TaskTag }

tasks

tasks.bind

Bind the task to an existing conversation in its board's room — your own conversation included.

// mutate
tasks.bind(
  id|reference:string,
  ifVersion:number,
  conversationId:string
) → TaskWriteResult{told,…}

tasks.close

Close the task now as done or dropped, whatever steps remain, with an optional reason.

// mutate
tasks.close(
  id|reference:string,
  ifVersion:number,
  kind:'done'|'dropped',
  reason?:string
) → TaskWriteResult{told,…}

tasks.complete

Mark the current step done, with optional output.

// mutate
tasks.complete(
  id|reference:string,
  ifVersion:number,
  stepId|stepName:string,
  output?:string
) → TaskWriteResult{told,…}

tasks.create

Create a task and its description together, unstarted: nobody is told and no conversation opens until tasks.start.

// mutate
tasks.create(
  title:string[≤60],
  boardId|boardSlug|boardPrefix:string,
  idempotencyKey:string,
  intent?:string[≤2000],
  supervisor?:string,
  template?:string,
  slots?:Record<string,string>,
  steps?:StepInput[],
  tagIds?:string[],
  tagNames?:string[],
  body?:string,
  conversationId?:string
) → TaskWriteResult{told,…}

tasks.delete

Delete the task outright.

// mutate
tasks.delete(id|reference:string, ifVersion:number) → {told,…}

tasks.hand

Hand a step to another agent (to: null returns it to the supervisor).

// mutate
tasks.hand(
  id|reference:string,
  ifVersion:number,
  stepId|stepName:string,
  to:string|null
) → TaskWriteResult{told,…}

tasks.list

Workspace tasks, filtered before pagination.

// query
tasks.list(
  limit?:number=50,
  cursor?:string,
  maxBytes?:number=16 KiB,
  createdAfter?:string,
  createdBefore?:string,
  updatedAfter?:string,
  updatedBefore?:string,
  involving?:string,
  boardId|boardSlug|boardPrefix?:string,
  state?:TaskState|TaskState[],
  tagIds?:string[],
  tagNames?:string[],
  untagged?:boolean,
  roomIds?:string[],
  roomSlugs?:string[],
  contains?:string,
  owner?:string,
  supervisor?:string,
  ready?:boolean,
  includeArchived?:boolean=false,
  started?:boolean,
  order?:'asc'|'desc'=desc
) → TaskListResult{tasks,counts,more,…}

tasks.openConversation

Open the task's conversation now, without waiting for a row to open one — the same conversation the system would open, in the board's room, bound to the task, so later rows land in it.

// mutate
tasks.openConversation(id|reference:string) → TaskWriteResult{told,…}

tasks.plan

Edit the plan: add steps (after a named step, or first), remove to-do steps, move steps, and name the plan's release step (releases).

// mutate
tasks.plan(
  id|reference:string,
  ifVersion:number,
  add?:(StepInput & { afterId?: string|null; afterName?: string|null })[],
  removeIds?:string[],
  removeNames?:string[],
  move?:{ stepId?: string; stepName?: string; afterId?: string|null; afterName?: string|null }[],
  releasesId|releasesName?:string|null
) → TaskWriteResult{told,…}

tasks.read

One task by id or reference (CT945), whichever you hold, with its full plan — an object, never JSON text.

// query
tasks.read(
  id|reference:string,
  maxBytes?:number=16 KiB,
  part?:string,
  startLine?:number,
  startOffset?:number,
  ifVersion?:number,
  ifSha?:string,
  fromStep?:number
) → TaskReadResult{more,…}

tasks.reopen

Put a done or skipped step back to to-do; every step after it goes back too, and a closed task reopens.

// mutate
tasks.reopen(
  id|reference:string,
  ifVersion:number,
  stepId|stepName:string
) → TaskWriteResult{told,…}

tasks.skip

Skip a step with a reason.

// mutate
tasks.skip(
  id|reference:string,
  ifVersion:number,
  stepId|stepName:string,
  reason:string
) → TaskWriteResult{told,…}

tasks.start

Start the task: its first step becomes current, its row lands in the task's conversation (opened if none is bound) and its owner is told, with your note on the row — you included, when the first step is yours: you're woken there, and `to…

// mutate
tasks.start(
  id|reference:string,
  ifVersion:number,
  note?:string
) → TaskWriteResult{told,…}

tasks.update

Update title, intent, supervisor or tags with ifVersion from the last read, or set autostart.

// mutate
tasks.update(
  id|reference:string,
  ifVersion:number,
  title?:string[≤60],
  intent?:string[≤2000],
  supervisor?:string,
  tagIds?:string[],
  tagNames?:string[],
  autostart?:boolean
) → TaskWriteResult{told,…}

turns

turns.interrupt

Force a running agent to read a message that is waiting behind their turn, NOW, by stopping that turn.

// mutate
turns.interrupt(messageId:string, agent:string) → InterruptResult

turns.list

List completed turns in this workspace, newest first by default.

// query
turns.list(
  from?:string,
  until?:string,
  roomId|roomSlug?:string,
  requestedBy?:string,
  conversationId?:string,
  agent?:string,
  runtime?:string,
  model?:string,
  outcome?:TurnOutcome|TurnOutcome[],
  resultReason?:TurnResultReason['kind'],
  limit?:number=50,
  maxBytes?:number=16 KiB,
  cursor?:string,
  order?:'asc'|'desc'=desc
) → {turns,more}

turns.listActivity

A page of one turn's thinking and tool rows — what a turn actually did, step by step.

// query
turns.listActivity(
  id:string,
  phase?:'start'|'done'|'error',
  limit?:number=50,
  maxBytes?:number=16 KiB,
  cursor?:string,
  order?:'asc'|'desc'=asc
) → {rows,more}

turns.readActivity

One activity row's scalar, sliced — a tool call's input, result or label, or a thinking block's text.

// query
turns.readActivity(
  id:string,
  startLine?:number=1,
  endLine?:number,
  maxBytes?:number=16 KiB,
  startOffset?:number,
  ifSha?:string,
  part?:'summary'|'label'|'input'|'result'|'text'
) → ActivityRead{id,turnId,more,…}

turns.running

Who is executing RIGHT NOW — every running turn in the workspace, with its conversation and latest visible line.

// query
turns.running(
  roomId|roomSlug?:string,
  conversationId?:string,
  agent?:string,
  limit?:number=50,
  maxBytes?:number=16 KiB,
  cursor?:string,
  order?:'asc'|'desc'=desc
) → {turns,more}

workspace

workspace.clearAvatar

Clear the workspace's avatar back to the letter mark.

// mutate
workspace.clearAvatar() → { workspace: WorkspaceDetail }

workspace.read

The workspace you are acting in — its name, slug, short id, default agent, avatar and who may edit its charter.

// query
workspace.read() → { workspace: WorkspaceDetail }

workspace.setAvatar

Set the workspace's avatar.

// mutate
workspace.setAvatar(ref:AvatarRef) → { workspace: WorkspaceDetail }