POST /v1/routines/waits/{token}
Answers the step: it completes with `{signal, payload, received_at, timed_out: false}`, where `payload` is `{decision, approved, comment, via: "link"}`, exactly as if the signal had been sent with `POST /v1/routines/runs/{run_id}/signal`. The first answer wins: the answer is delivered only while the step waits (never buffered), and any later one answers 410 `wait_already_answered`. A step that has not started waiting yet answers 409 `wait_not_ready` with `Retry-After`; a step past its deadline or 7 days, or a run that ended, answers 410 `wait_link_expired`. The page posts its form (`application/x-www-form-urlencoded`) and gets the page back; programs send JSON and get `RoutineWait`.

Path Parameters

token string required path
The link's token, `<run id>.<step id>.<signature>` (32 base64url characters), from `run.wait_urls.<step id>`. Anything else answers `wait_link_invalid`, so a link cut short in a message still gets the page that says so.
Example: "run_01m6f0re3gzz9kvb26hebdcd0x.approve.q3Zr0wq9mXk3o1yJb7vH2cT5dN8pLa4e"

Request Body required

application/json
decision string REQUIRED
Enum: approve, reject
comment string

Responses

200 The step, answered.
application/json
object string REQUIRED
Enum: routine_wait
status string REQUIRED
waiting: it takes an answer. pending: the step has not started waiting. answered: through a link or a signal. timed_out: its deadline passed. expired: 7 days after it started waiting. ended: the run ended, or the step was skipped or canceled, before an answer.
Enum: waiting, pending, answered, timed_out, expired, ended
run_id string REQUIRED
run_status string REQUIRED
Enum: queued, running, succeeded, failed, canceled
routine_id string REQUIRED
routine_name string | null
step_id string REQUIRED
step_name string | null
prompt object | null REQUIRED
The step's evaluated prompt while it waits, and after an answer while the node still holds the run (null once the run is only in the run store).
title string
body string
image string (uri)
link string (uri)
dropped string[]
Array of: string
waiting_since string (date-time) | null
expires_at string (date-time) | null
While the step waits; its own deadline may end it sooner.
answer object | null REQUIRED
Once answered. `decision` is null for a signal sent through the API without one.
decision string | null
Enum: approve, reject, null
approved boolean | null
comment string | null
via string
Enum: link, signal
answered_at string (date-time) | null
400 The request failed validation (`invalid_request`, `invalid_url`).
404 The link is not valid (`wait_link_invalid`).
409 The step has not started waiting yet (`wait_not_ready`).
410 The link no longer takes an answer (`wait_already_answered`, `wait_link_expired`).
413 The request body exceeds this operation's limit.
415 The Content-Type is not accepted by this operation.
429 A rate, concurrency or quota limit applies (`rate_limited`, `concurrency_limited`, `quota_exceeded`).
500 Something failed inside B3OS (`internal`). Retry with backoff.
503 The node or the product is not available right now (`node_unavailable`, `product_unavailable`, `capacity_unavailable`).
curl -X POST 'https://beta-api.b3os.org/v1/routines/waits/run_01m6f0re3gzz9kvb26hebdcd0x.approve.q3Zr0wq9mXk3o1yJb7vH2cT5dN8pLa4e' \  -H 'Authorization: Bearer YOUR_API_TOKEN' \  -H 'Content-Type: application/json' \  -d '{  "decision": "approve",  "comment": "Ship it."}'
const response = await fetch('https://beta-api.b3os.org/v1/routines/waits/run_01m6f0re3gzz9kvb26hebdcd0x.approve.q3Zr0wq9mXk3o1yJb7vH2cT5dN8pLa4e', {  method: 'POST',  headers: {      "Authorization": "Bearer YOUR_API_TOKEN",      "Content-Type": "application/json"  },  body: JSON.stringify({    "decision": "approve",    "comment": "Ship it."  })});const data = await response.json();console.log(data);
import requestsheaders = {    'Authorization': 'Bearer YOUR_API_TOKEN'}response = requests.post('https://beta-api.b3os.org/v1/routines/waits/run_01m6f0re3gzz9kvb26hebdcd0x.approve.q3Zr0wq9mXk3o1yJb7vH2cT5dN8pLa4e', headers=headers, json={  "decision": "approve",  "comment": "Ship it."})print(response.json())
200 Response
{  "object": "routine_wait",  "status": "answered",  "run_id": "run_01m6f0re3gzz9kvb26hebdcd0x",  "run_status": "running",  "routine_id": "rtn_01m5t6vhkw4a8fa4tb5w4ffz37",  "routine_name": "launch-card",  "step_id": "approve",  "step_name": "Approve the card",  "prompt": {    "title": "Post the launch card to",    "body": "The card for 0.4.0 is below. Approve posts it; Reject stops the run here.",    "image": "https://usercontent.beta.b3os.org/render/art_01m6f0rf6q8f3d8wq7xr7d1f3p/card.png"  },  "waiting_since": null,  "expires_at": null,  "answer": {    "decision": "approve",    "approved": true,    "comment": "Ship it.",    "via": "link",    "answered_at": "2026-11-02T22:11:40.512Z"  }}
Ask a question... ⌘I