Approve or refuse a run step
Records a person's decision on one approval and releases the run step it was raised for. A step the plan marks as costing credits, needing a signature, publishing, or reading a secret DOES NOT RUN until a decision approves it — this endpoint is what ends that wait, so the approval card in the dashboard is the control that runs the work, not a record of it. What the caller must know: the run is resolved through its project, so a run owned by another user answers the same 404 as a run that does not exist — never a 403, which would confirm the run is real. The approval id must belong to the run in the path; one raised by a different run is the same 404. `approved: true` releases the step so the run performs it at its next check of the gate, which is the step after the one it was refused on; a run whose budget is already spent, or that has finished, will not come back to it — read the run's events to see what the run did with the answer. The decision is final. An approval already decided answers 409, because two people looking at the same card and both pressing a button must not produce two answers with different timestamps. Decide the approval, then re-read `GET /projects/{projectId}/runs/{runId}/events` to see the run's reaction rather than assuming the step ran.
Records a person's decision on one approval and releases the run step it was raised for. A step the plan marks as costing credits, needing a signature, publishing, or reading a secret DOES NOT RUN until a decision approves it — this endpoint is what ends that wait, so the approval card in the dashboard is the control that runs the work, not a record of it.
What the caller must know: the run is resolved through its project, so a run owned by another user answers the same 404 as a run that does not exist — never a 403, which would confirm the run is real. The approval id must belong to the run in the path; one raised by a different run is the same 404. approved: true releases the step so the run performs it at its next check of the gate, which is the step after the one it was refused on; a run whose budget is already spent, or that has finished, will not come back to it — read the run's events to see what the run did with the answer.
The decision is final. An approval already decided answers 409, because two people looking at the same card and both pressing a button must not produce two answers with different timestamps. Decide the approval, then re-read GET /projects/{projectId}/runs/{runId}/events to see the run's reaction rather than assuming the step ran.
Path Parameters
The project the run belongs to.
The run that raised the approval.
The approval to decide. The tool.result event raised by a gated step names it, and the plan event names which steps expect one.
Request Body
application/json
The decision. true releases the step, false refuses it.
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/projects/string/runs/string/approvals/string" \ -H "Content-Type: application/json" \ -d '{ "approved": true }'{ "id": "string", "runId": "string", "stepId": 0, "type": "cost", "status": "pending", "payload": { "property1": null, "property2": null }, "createdAt": "string", "decidedAt": "string"}Cancel a run POST
Requests cancellation of a queued or running run. A queued run is cancelled immediately. A running run is marked cancelled and the worker observes it at its next heartbeat, so this call does not wait for the agent to stop. Cancelling a run that has already finished is a no-op that returns the finished run unchanged, so a client that cancels twice does not get an error.
Read a run's events GET
Returns the run's event log in order, starting after `afterSeq`. This is the paged counterpart to the SSE stream and is the right choice for a client that cannot hold a long-lived connection. The `nextCursor` it returns is exactly the value to send as the next `afterSeq`, or as `Last-Event-ID` when switching to the stream, so the two never skip or repeat an event. `done` is true once the run has reached a terminal state, which is the signal to stop polling.