v2.0.0next
HTTP API

Filing, reading and changing work items

The board's routes for filing a work item, duplicating it, reading the board and one item, changing an item, and acting on several at once.

This page lists the board's routes for filing, reading and changing a work item. The routes that decide a gate, pause, resume, skip, escalate, end and retry an item are on Gates, run control, ending and retry. The shapes the routes answer with are on The work item object.

Every route under /api/ except the stable routes, and the /api/ws/events WebSocket, exists for the board and can change in any release. For those, prefer the kraft command with --json, which is stable.

RouteWhat it does
POST /api/work-itemsFiles a work item, paused unless the body starts it.
POST /api/work-items/{id}/duplicateFiles a fresh paused item from an existing one.
GET /api/work-itemsLists the items on the board.
GET /api/work-items/{id}Answers one item in full.
PATCH /api/work-items/{id}Changes an item's title, description, chain, overrides, budget, policy or attachments.
POST /api/work-items/bulkPauses, cancels, archives or restores several items.

Routes that read an item's budget, events, diff and documents are on Budget, events, diff and documents.

Filing a work item

POST /api/work-items

Files a work item. The item is paused unless the body sets autostart: true, like kraft item create without --autostart. POST /api/work-items?dry_run=1 runs the same checks and files nothing: see Dry run.

FieldTypeDefaultMeaning
titlestringrequiredOne line of plain text, up to 500 characters, with no tab, escape or other control character.
descriptionstring""The brief: prose the spec node writes a design from.
repostringrequiredThe absolute path of a connected repo.
chainstringthe repo's default_chainThe chain to run. The pre-2.0 name chain_template is still read, by PATCH too.
workspace, members, root_pointer_policystring, list, stringnull, [], nullA workspace item: the workspace repo is the root of, the members it selects, and ignore or bump. An unset policy is the workspace's root_pointer_default.
base_branchstringnullThe branch the work starts from and the merge request targets: the repo's default branch when null. Refused unless origin has it.
attachmentslist[]Existing documents, {kind, path} with kind spec or plan and path inside the repo. At most one per kind. They trim the gates they satisfy.
cwdstringnullThe caller's working directory, sent only with attachment paths. A path is also read from another working tree of the same repo there.
autostartbooleanfalseStart the item now instead of filing it paused.
auto_gatebooleantrueArms agent review for the item's auto_escalate gates.
implements_beadslist[]Bead ids the item implements, closed when it completes.
skip_nodeslist[]Node ids to drop from the chain. An id that is no node of the chain is refused.
budget_usdnumber or nullthe policy defaultThe item's own dollar cap. Omitted uses the policy default, null means no cap.
node_overridesobject{}{node_id: {field: value}}, as PATCH takes it.
policyobjectnullThe item's own policy override.

A POST or PATCH body naming two different chains under chain and chain_template is refused with 422, its detail one sentence naming both. The same chain twice is fine.

The answer is 201 with {"id": "...", "status": "paused"} for an item filed paused, and also when autostart: true found every slot busy. These keys appear only when they apply:

KeyWhen
slotsautostart: true was sent but every slot (max_concurrent) was busy, so the item was filed paused: {"busy": 3, "limit": 3}.
repo_warningThe repo declares no setup command, or no test command nor test scopes. It says where the item will stop.
duplicate_warningAnother open item has the same title or implements a bead the body names.
bead_warningWhy no bead was filed for the item.

An item that starts at once answers 201 with bead_id, chain_definition, materialized_chain and current_node_id beside id and status.

StatusWhen
503policy.yaml does not load. It is checked first, before the 403. Kraft refuses new work until the file loads.
403autostart: true came on a request that carries X-Kraft-Session-Id (a worker session) or X-Kraft-Client: mcp (the MCP server). An agent files work paused, and you start it.
503The library or a chain does not load.
422A field is refused: a relative or unconnected repo, an unknown chain, a title or description that fails its check, a missing attachment, an unknown skip_nodes id, an unknown base_branch, or an override the chain or policy refuses.
502Filing the item failed after the checks passed.

The server cannot tell a coding agent that runs kraft item create --autostart in its own shell from a person, so that call starts the item.

Every repo path you send must be absolute: the server would read a relative one against its own directory, not yours. POST /api/work-items, POST /api/repos and POST /api/triggers refuse a relative path with 422, and PATCH or DELETE /api/repos?path= answer 404.

POST /api/work-items/{id}/duplicate

Files a fresh, paused item from the item {id}: its title, description, repo, chain, workspace selection and attachments, snapshotted again under the new item's own id. Any source status is accepted, archived and cancelled included. No run state, override, policy override, budget or bead link carries over. This is a fresh intake, not a clone of the row. Agent review of the item's auto_escalate gates is armed, as for an ordinary create, whatever the source had. The body is empty.

{"id": "...", "status": "paused", "duplicate_warning": "..."}

duplicate_warning is present only when another open item looks like a duplicate, the same check POST /api/work-items makes.

StatusWhen
404There is no such item.
409The source's stored attachment copy has gone missing, as it does when the source is abandoned. The answer names it, before anything is filed.
422The chain or workspace the source used is gone, or its title is empty.
502Filing the item failed after the checks passed.
503policy.yaml, the library or a chain does not load.

Reading work items

GET /api/work-items

Answers the board: {"items": [...], "cursor": N}. cursor is the highest event seq at the time of the read. Each row is the list row: a short subset of the item's fields, with display_status, stop (without facts, task and attempt), step, mr_ref and progress (without tasks) in place of the detail's per-row lookups. It carries no agent_overrides, node_overrides or policy_override.

QueryMeaning
archived=trueOnly archived items. Without it, archived items are left out. An archived item that was cancelled or abandoned also needs include_abandoned=true.
include_abandoned=trueInclude cancelled and abandoned items. Without it they are left out, archived or not.

GET /api/work-items/{id}

Answers one item as the detail object, and 404 for an id that names no item.

Changing a work item

PATCH /api/work-items/{id}

Changes the fields the body names and leaves the rest. A body naming none is refused with 422. The write is one transaction, so a body that changes several fields never lands half.

FieldTypeMeaning
titlestringThe new title. It cannot be empty and is one line.
descriptionstringThe new brief. "" clears it.
chainstringSwitches a not-yet-started item onto that chain.
agent_overridesobjectReplaces the item's whole model and effort override. See below.
node_overridesobjectMerges per node. See below.
budget_usdnumber or nullThe item's own dollar cap. null sets no cap, and leaving the field out leaves the cap alone.
policyobjectReplaces the item's policy override. {} clears it. A change applies from the next node the item enters and the next time it observes a wait.
attachmentsobject{"spec": PATH} snapshots that kind again, {"spec": null} drops it and puts back the gate it trimmed. A kind not named keeps its copy.
cwdstringThe caller's working directory, as at create.

agent_overrides replaces the override as a whole. A field you leave out, or send as null, is dropped, and {} clears the override.

node_overrides merges per node. {"plan": {"effort": "high"}} sets that field and keeps the node's others, {"plan": {"effort": null}} drops one field, and {"plan": {}} drops the node's overrides. {} resets every node.

The answer is {"id": "...", ...} with each field you sent. A chain you sent is echoed as chain_template. agent_overrides and node_overrides are echoed as stored: {} once an override is cleared, and for node_overrides only the nodes you named.

StatusWhen
403A worker session sent attachments, an override, a policy or a budget_usd for its own item. An escalation turn is refused policy and budget_usd too.
404There is no such item, or chain names a chain that does not exist.
409The item has ended.
409chain or attachments was sent for an item that has started: its chain and documents are fixed for its life. Another write changed them meanwhile.
409node_overrides names a node that has started, or is {} for an item that has.
422The body names no field to change.
422A field is refused, and detail says which: an unknown node id or field, a model or effort the harness refuses, a limit above the policy's maxima, or a path that is not a file in the repo.
422auto_escalate: true on a gate that declares no auto_review.

A started node accepts one exception. On an item stopped at that node because its stored model or escalate_model is not a model id, clearing that node ({"<node>": {}}) and changing model, escalate_model or effort alone are accepted, so the stop can be repaired.

POST /api/work-items/bulk

Applies one action to several items, each through the single-item route, in ids order, each in its own write.

{"action": "pause", "ids": ["...", "..."], "reason": "..."}
FieldTypeMeaning
actionstringpause, cancel, archive or restore.
idslistFrom 1 to 200 item ids.
reasonstringRequired when action is cancel, and checked before anything is touched (422 otherwise). The other actions ignore it.

The answer is one result per id, in order:

{"results": [{"id": "...", "ok": true, "status": "paused"}, {"id": "...", "ok": false, "error": "..."}]}

An id whose action raises, an item in the wrong status for example, becomes {id, ok: false, error, status}, and the rest of the batch still runs. An id that names no item answers {id, ok: false, error: "not found"} with no status. status is the item's stored status after the action ran.

StatusWhen
422action is none of the four, ids holds fewer than 1 or more than 200 ids, or action is cancel and reason is blank. Nothing is touched.
  • Gates, run control, ending and retry: the routes that decide, pause, resume, skip, escalate, end and retry an item.
  • The work item object: the stored row, the list row and the detail object these routes answer with.
  • Item verbs: the kraft item command behind each action here, with its flags and output.
  • MCP tools: which of these an agent may call on its own item.
  • Agents and gates: why a worker session is refused some of these, and what that check does not stop.
Copyright © 2026