# Human waits

> Store decision context and pause a job until an authenticated operator responds.

`HandlerContext.waitForHuman` parks a job without consuming its logical attempt. It stores bounded
JSON context so an operator can understand the decision, then returns the retained result after the
handler restarts.

<Tabs items={["TypeScript", "Python", "Go"]}>
  <Tab value="TypeScript">
    ```ts
    const review = await context.waitForHuman<
      { accountId: string; prompt: string },
      { approved: boolean }
    >("account-review", { accountId, prompt: "Approve this account?" });

    if (review.approved) await activateAccount(accountId);
    ```

  </Tab>
  <Tab value="Python">
    ```python
    review = context.wait_for_human(
        "account-review",
        {"accountId": account_id, "prompt": "Approve this account?"},
    )
    if isinstance(review, dict) and review.get("approved") is True:
        activate_account(account_id)
    ```
  </Tab>
  <Tab value="Go">
    ```go
    value, err := handler.WaitForHuman("account-review", map[string]any{
        "accountId": accountID,
        "prompt":    "Approve this account?",
    })
    if err != nil {
        return nil, err
    }
    review, ok := value.(map[string]any)
    if ok && review["approved"] == true {
        activateAccount(accountID)
    }
    ```
  </Tab>
</Tabs>

Code before the wait runs again, so checkpoint earlier effects or make them idempotent.

Go handlers can pass `ExternalWaitOptions` when the decision needs a shorter lifetime. Python
handlers pass `timeout_ms` for the same boundary.

## Complete a decision

The dashboard marks pending decisions in the `Waiting` task list. Its server replaces browser
attribution with the authenticated principal before calling the queue mutation.

The `Waiting` task filter includes open signal and human-decision waits. Dependency and child-join
work stays under `Blocked`, because operator input cannot resume it.

An application can put `dashboard.quickAction` in the stored context with a menu `label` and JSON
`result`. The task menu asks the operator to confirm that result before completion. It never invents
a quick action for generic decisions.

Applications can call `Queue.completeHumanWait(jobId, name, result, request)` after enforcing their
own authorization. The request records its trusted actor as `requestedBy`. The first accepted result
resumes the job. An equivalent retry returns the retained result, while a competing completion
cannot overwrite the accepted audit evidence.
The queue response exposes that accepted decision as `payload`, matching signal delivery.

Go applications complete decisions through `Queue.CompleteHumanWait` with
`ExternalWaitDelivery`. The result retains the accepted decision and actor.

`Admin.listHumanWaits()` exposes the same paginated projection for custom operator tools. A timeout,
job deadline, or cancellation closes the decision and makes late completion stale.

TypeScript handlers call `ctx.waitForHuman` and can pass `timeoutMs`. Go exposes the same boundary
as `HandlerContext.WaitForHuman`. Both return a `nextCursor`. The completion response uses the same
retained-payload shape as `Queue.sendSignal`, although human decisions have their own completion
method and authorization path.

## Next

- [Dashboard](/docs/dashboard) — protect operator mutations
- [Signals](/docs/signals) — resume from an application-owned event
- [Durable execution](/docs/durable-execution) — understand handler replay

---

Exact human-wait statuses, bounds, and transitions:
[architecture reference](https://github.com/stablemates/workhorse/blob/main/docs/architecture.md#human-decision-suspension).
