Workbenches is an open standard. Read why
Docs Commands

API reference

Commands

POST /v1/runs/:id/commands

Key runs:write, or session: member. On a personal worker, send, steer, queue, and answer are refused with 403 for anyone but the worker's owner; any member may cancel.

Every command has a kind, a payload, and an idempotency_key (1 to 200 characters). A repeated key returns the original command.

kind payload Effect
send { "text": "..." } Start a new turn when the runner is idle.
queue { "text": "..." } Run the text as the next turn, immediately when idle.
steer { "text": "..." } Add the text to the turn in progress.
answer { "request_id": "...", "response": ... } Resolve a permission or question the runner is waiting on.
cancel {} Stop the runner and end the run as cancelled.

text is 1 to 100,000 characters.

curl https://api.workbenches.dev/v1/runs/wb_.../commands \
  -H "Authorization: Bearer $WORKBENCHES_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "send", "payload": { "text": "Also check the rollback." }, "idempotency_key": "followup-1" }'

Response 202:

{
    "command": {
        "id": "wbc_...",
        "run_id": "wb_...",
        "kind": "send",
        "payload": { "text": "Also check the rollback." },
        "idempotency_key": "followup-1",
        "status": "pending",
        "reason": null,
        "created_at": "...",
        "delivered_at": null
    },
    "after_sequence": 42
}

Read events after after_sequence to see the effect.

Answers#

request_id is the ID of the pending request, from the input.requested or question.requested event. response is:

  • for a permission: allow, deny, or an offered option,
  • for a question: a string for a single question, or an array of string arrays, one per question, such as [["Yes"], ["Staging", "Production"]].

Strings are 1 to 100,000 characters; an array holds up to 100 arrays of up to 100 strings of up to 10,000 characters.

A runner can withdraw a request it no longer needs. The run then records input.rejected (for a permission) or question.rejected with code: "request_withdrawn", goes back to running, and a later answer to it is rejected because the request is no longer pending.

Command lifecycle#

A command is stored first and delivered by the run's coordinator:

status Meaning
pending Stored, not yet picked up. A command that arrives before the runner is up waits.
claimed Picked up for delivery.
delivered Given to the runner. delivered_at is set.
rejected Refused, with a reason, for example when the runner is still responding or the run has ended.

A command to a run that has ended answers 409.

Errors#

Status When
400 The body does not match a command shape.
403 Outside the run's organization, missing runs:write, or a non-owner on a personal worker.
404 No such run.
409 The run has ended.