> ## Documentation Index
> Fetch the complete documentation index at: https://usefused.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Approvals and client tools

> Handle required actions and resume suspended invocations over JSON, SSE, or WebSocket.

Approvals and client-hosted tools suspend the current invocation before external input is needed.

| Required action    | JSON              | SSE or WebSocket        | Resume with                                |
| ------------------ | ----------------- | ----------------------- | ------------------------------------------ |
| Human approval     | `requires_action` | `approval.requested`    | HTTP decision or `approval.decision` frame |
| Client-hosted tool | `requires_action` | `client_tool.requested` | HTTP result or `client_tool.result` frame  |

## Decide an approval

<Tabs>
  <Tab title="HTTP approve">
    ```http theme={null}
    POST /approvals/{approvalId}
    Content-Type: application/json

    {"decision":"approve"}
    ```
  </Tab>

  <Tab title="HTTP deny">
    ```http theme={null}
    POST /approvals/{approvalId}
    Content-Type: application/json

    {"decision":"deny"}
    ```
  </Tab>

  <Tab title="WebSocket">
    Reply on the active `/live` connection with the response and approval IDs issued by Harnest:

    ```json theme={null}
    {"type":"approval.decision","responseId":"resp_...","approvalId":"approval_...","decision":"approve"}
    ```

    Harnest commits the decision, sends `approval.resolved`, and only then resumes execution on that socket. Use `decision: "deny"` to reject the action.
  </Tab>
</Tabs>

The HTTP endpoint remains available when the approving interface does not own the live socket. Both paths apply the same user, response, approval, expiry, and one-time-use checks.

## Approval outcomes

| Outcome               | Runtime behavior               |
| --------------------- | ------------------------------ |
| Approved              | Resume the suspended task      |
| Denied or expired     | Do not run protected code      |
| Reused decision       | Reject the consumed approval   |
| Binding changed       | Reject the mismatched approval |
| Later protected block | Create another request         |

Harnest does not restart the tool or replay earlier work.

## Recover a response

Every neutral JSON, SSE, and WebSocket invocation has a response ID. Poll it with the same authenticated user and session:

```http theme={null}
GET /responses/{responseId}?sessionId={sessionId}
```

The response reports the latest `in_progress`, `requires_action`, `completed`, `denied`, `cancelled`, or `failed` state. A `requires_action` response includes the approval or client-tool ID, so a client can recover exact task correlation without guessing from the latest chat message.

With a Harnest-owned checkpointer, Harnest stores the completed response envelope before marking the run terminal. Another replica can therefore return the same completion. Pending human approvals and client-tool exchanges remain process-local and must return to the process that owns the suspended execution.

In advanced mode, Harnest provides the response receipt and WebSocket protocol on a best-effort basis. If you replace Harnest's graph, checkpointer, transport, or tool wiring, Harnest does not rewrite that boundary and cannot guarantee cross-process recovery.

## Submit a client-tool result

<Tabs>
  <Tab title="HTTP">
    ```http theme={null}
    POST /client-tools/{requestId}
    Content-Type: application/json

    {"output":{"title":"Harnest documentation"}}
    ```
  </Tab>

  <Tab title="WebSocket">
    Reply to `client_tool.requested` with a `client_tool.result` frame using the same request ID.
  </Tab>
</Tabs>

Submitted output is identity-bound, consumed once, and validated before execution resumes.

For [`@client_input`](/docs/harnest/build/agent-tools/client-tools#private-client-input), the action also includes `privateInput: true` and `inputSchema`. Send the private form through the same HTTP `output` field or WebSocket result frame. Harnest delivers it only to the application handler and returns the separately authored public response. Keep the submitted value out of client transcripts and telemetry. [AG-UI clients](/docs/harnest/runtime/ag-ui/interactions#private-client-input) use an explicit interrupt resume.

<CardGroup cols={2}>
  <Card title="Human approval authoring" icon="user-check" href="/docs/harnest/build/agent-tools/human-approvals">
    Choose always-on or dynamic approval and bind it to the exact operation.
  </Card>

  <Card title="Client-hosted tools" icon="display" href="/docs/harnest/build/agent-tools/client-tools">
    Declare typed work owned by the connected client.
  </Card>
</CardGroup>

<Note>
  Development stores are process-local. Durable terminal recovery needs a Harnest-owned checkpointer; resuming an unfinished action also needs the process that owns that suspension.
</Note>
