stackbone hitl
Every verb targets one installation: the local-dev install by default, any install with
--agent <id>.listis cursor-paginated.approveandrejectare destructive and require--yes.
Human-in-the-loop approvals for the targeted installation. This inbox is the
return path for a parked durable run: when a workflow step calls
requestApproval() (from @stackbone/sdk/workflow, see
Workflows → human-in-the-loop), the run pauses and
the pending approval surfaces here. The same inbox carries defineHook pauses
and any tool call an agent parked for a human decision. Deciding an approval
(approve or reject) resumes the parked run with your decision. An approval's
runId field links back to the run you can follow with
stackbone runs get.
Editing an approval payload is a Studio-only action. The CLI sends only the decision and an optional comment.
| Command | Purpose |
|---|---|
stackbone hitl list |
List approvals. Filters: --status <pending|approved|rejected|timed_out|cancelled>, --limit/--cursor. |
stackbone hitl get <id> |
Inspect one approval (topic, run, timeout, and its audit trail of past decisions). |
stackbone hitl approve <id> |
Approve a pending approval (--reason), resuming the parked run. Requires --yes. |
stackbone hitl reject <id> |
Reject a pending approval (--reason), resuming the parked run. Requires --yes. |
The runtime mints an approval id (a UUID) when it creates the pause.
stackbone hitl list
The human-in-the-loop approvals inbox: the return path for
requestApproval() (and defineHook) calls made
from your workflows, plus any tool call an agent paused for a human decision.
Each pending entry is a step waiting on a human decision.
| Flag | Type | Description |
|---|---|---|
--agent |
string | Installation id to target. Defaults to the local-dev install. |
--status |
string | Filter by status: pending, approved, rejected, timed_out, cancelled. |
--cursor |
string | Opaque page cursor (nextCursor / prevCursor). |
--limit |
string | Maximum number of approvals to return (1-100). |
Human mode prints one line per approval (id, status, topic, created-at), and
No approvals found. when the page is empty.
JSON payload
{
"schema_version": 1,
"items": [
{
"id": "3f6b0a1e-7c4d-4b21-9e88-2a5f1c0d9a44",
"status": "pending",
"topic": "refund",
"createdAt": "2026-06-01T10:00:00Z",
},
],
"nextCursor": "eyJ..." /* or null */,
"prevCursor": null,
}stackbone hitl get
Read one approval, including its audit trail of past decisions.
| Flag | Type | Description |
|---|---|---|
--agent |
string | Installation id to target. Defaults to the local-dev install. |
Human mode prints the record as labelled lines: id, status, topic, run,
created, timeout, decided, and a decisions count. Those labels are the
human view; the JSON keys are the wire names below.
JSON payload
{
"schema_version": 1,
"approval": {
"id": "3f6b0a1e-7c4d-4b21-9e88-2a5f1c0d9a44",
"status": "pending",
"topic": "refund",
"runId": "0a9d2c17-5f83-4d0e-8f61-9c2b7e4a1d33" /* or null */,
"createdAt": "2026-06-01T10:00:00Z",
"timeoutAt": "2026-06-02T10:00:00Z" /* or null */,
"decidedAt": null,
"fallback": "reject" /* the expiry policy, or null */,
"decisions": [],
},
}The record also carries agentId, workspaceId, requestedByStepId,
payload, schema, callbackUrl and metadata.
stackbone hitl approve
Decide a pending approval, resuming the durable workflow or agent turn that is
parked on it. Destructive: requires --yes. --reason attaches an optional
comment, recorded on the decision.
stackbone hitl approve 3f6b0a1e-7c4d-4b21-9e88-2a5f1c0d9a44 --yes
stackbone hitl approve 3f6b0a1e-7c4d-4b21-9e88-2a5f1c0d9a44 --yes --reason "Verified with the customer"| Flag | Type | Description |
|---|---|---|
--agent |
string | Installation id to target. Defaults to the local-dev install. |
--reason |
string | Note recorded with the decision. |
Human mode prints Approved approval <id>.
JSON payload
{
"schema_version": 1,
"approval": { "id": "3f6b...", "status": "approved", "decidedAt": "2026-06-01T11:00:00Z" },
"decision": {
"id": "b2c8...",
"approvalId": "3f6b...",
"decision": "approve",
"comment": "Verified with the customer" /* or null */,
"actorEmail": "you@example.com" /* or null */,
"decidedAt": "2026-06-01T11:00:00Z",
},
}The approval object is the same record hitl get returns, minus the
decisions trail.
stackbone hitl reject
The mirror of approve: aborts the parked run's approval with a reject
decision. Destructive: requires --yes. Same flags, same JSON payload, with
decision set to reject.
stackbone hitl reject 3f6b0a1e-7c4d-4b21-9e88-2a5f1c0d9a44 --yes --reason "Out of policy"Human mode prints Rejected approval <id>.
Timeouts
The runtime's timeout sweep closes a pending approval as timed_out once its
timeoutAt deadline passes with no decision. The sweep records the expiry
policy the step declared as a system decision, then applies it to whatever the
approval was blocking. The policy is reject unless the step said otherwise
(fallback on a workflow's requestApproval(), onTimeout on
stackbone.approval.request()).
An approval created with onTimeout: 'ignore' carries no policy. Its fallback
is null and the sweep skips it. It stays pending until a person decides it,
whatever its timeoutAt says.
Exit codes: 0 ok · 3 no target (no project and no --agent, or
stackbone dev is not running) · 4 not found (unknown approval, or a targeted
box with no registered deployment) · 5 permission (approve/reject without
--yes) · 1 generic. Only a pending approval can be decided, so approve
and reject also exit 1 on an approval somebody already decided, and on one
the timeout sweep already closed. The full table is in
Shared conventions.