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

# Human-in-the-loop oversight

> Supervise a live AI-agent conversation — whisper guidance, approve high-risk tool calls, and take over — across voice and chat.

# Human-in-the-loop oversight

An AI agent handles most conversations end to end, but you stay in control of the ones that matter. From a live conversation you can do three things without disconnecting the customer:

* **Whisper** — send the agent a private instruction it reads on its next turn.
* **Approve a high-risk action** — a tool you marked as sensitive pauses for your sign-off before it runs.
* **Take over** — pause the AI and let a human handle the conversation, then hand control back.

All three work on both channels the agent runs on: **voice** calls and **chat** conversations (web chat, SMS, WhatsApp, and the other messaging channels).

## Prerequisites

* The conversation must be live. Whisper and take-over apply to an agent conversation that is `active`, `open`, or `escalated`; a closed conversation returns `404`.
* These are supervisor actions. Sending a whisper is available to the `owner`, `admin`, and `supervisor` roles; approving or rejecting a tool call is available to `owner` and `admin`.
* Authenticate every request with an API key (`X-API-Key: dv_live_sk_...`) or a dashboard session, exactly as with the rest of the API.

## Whisper: guide the agent without taking over

A whisper is a one-way note from you to the agent. The agent reads it at the start of its next turn and factors it into that reply — you are steering the model, not messaging the customer. The customer never sees the note.

Send a note:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/agents/conversations/:conversationId/supervisor-notes \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "note": "Offer the annual plan discount before they ask to cancel." }'
```

A note is 1–1000 characters. An empty note or one over the limit returns `400`; a conversation that is not live returns `404`. On success you get `201` with the created note id:

```json theme={null}
{ "id": "supnote_...", "conversation_id": "conv_..." }
```

The agent applies the note on its **next** turn — so whisper while the conversation is still going. If the conversation ends before the agent takes another turn, the note is never applied and stays marked unread in the history.

Read the recent notes for a conversation (newest first, last 20) to build an oversight history panel:

```bash theme={null}
curl https://api.orbit.devotel.io/api/v1/agents/conversations/:conversationId/supervisor-notes \
  -H "X-API-Key: dv_live_sk_..."
```

Each note carries a `consumed_at` timestamp — `null` means the agent has not read it yet, a timestamp means it was picked up on the turn at that time.

## Approve a high-risk action

Mark any tool as sensitive by setting its `confirmation` to `"always"`. When the agent decides to call that tool, the call does **not** run: the turn pauses, the agent receives a blocked result instead of the tool output, and a pending approval is queued for a human to decide. Use this for irreversible or costly actions — issuing a refund, cancelling an account, sending a contract.

Review the queue:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/agents/tool-approvals?status=pending&limit=50" \
  -H "X-API-Key: dv_live_sk_..."
```

Each row shows what the agent wanted to do — `tool_id`, the `tool_args` it proposed, the `conversation_id` and `agent_id`, when it was `requested_at`, and a `cost_estimate` when the tool priced the call in advance. Filter by `status` (`pending`, `approved`, or `rejected`).

Approve it to let the action proceed. A `reason` is optional on approve:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/agents/tool-approvals/:id/approve \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Verified the customer on the call." }'
```

```json theme={null}
{ "approval_id": "tapprv_...", "status": "approved", "resumed": true, "warning": null }
```

Approving re-queues the deferred turn so the agent resumes and can complete the action. `resumed: true` confirms the resume job was accepted. If the resume queue is briefly unavailable, your decision is still saved (`resumed: false` with a `warning` string) and the platform retries — the approval record is the source of truth, so no decision is ever lost.

Reject it to block the action. A `reason` is **required** on reject so the decision is documented:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/agents/tool-approvals/:id/reject \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Refund exceeds the amount on the order." }'
```

```json theme={null}
{ "approval_id": "tapprv_...", "status": "rejected" }
```

Common responses:

| Status | Meaning                                                                                                                |
| ------ | ---------------------------------------------------------------------------------------------------------------------- |
| `404`  | No approval with that id in your organization.                                                                         |
| `409`  | The approval was already approved or rejected — no change is made (treat as "already decided", not a retryable error). |
| `422`  | You called reject without a `reason`.                                                                                  |

Every approve and reject writes an audit record, so you keep a reviewable trail of who decided what and why.

## Take over: pause the AI and step in

When a conversation needs a person, pause the agent and take the wheel. The customer stays on the same call or thread — only who is answering changes.

### Chat

Hand the conversation to a human, which pauses the agent so it stops replying:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/conversations/:id/handoff \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Customer asked for a manager." }'
```

Check who is currently answering — the agent-runtime reads this before every turn, and you can render a "human is handling this" banner from it:

```bash theme={null}
curl https://api.orbit.devotel.io/api/v1/conversations/:id/agent/active \
  -H "X-API-Key: dv_live_sk_..."
```

```json theme={null}
{ "agent_active": false, "agent_id": "agent_..." }
```

`agent_active: false` means a human has the conversation and the AI will not reply. When you are done, hand control back so the agent processes the next inbound message again:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/conversations/:id/agent/resume \
  -H "X-API-Key: dv_live_sk_..."
```

### Voice

On a live call, pause the AI mid-conversation so a supervisor or human agent can take over the call leg:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/voice/calls/:id/ai-pause \
  -H "X-API-Key: dv_live_sk_..."
```

The AI stops speaking on that call while a human continues it.

## Roles at a glance

| Action                            | Roles                          |
| --------------------------------- | ------------------------------ |
| Send / read whisper notes         | `owner`, `admin`, `supervisor` |
| Approve / reject a tool call      | `owner`, `admin`               |
| Hand off, resume, or pause the AI | `owner`, `admin`               |

## See also

* [Creating Agents](/agents/creating-agents) — configure a tool's `confirmation` to require approval.
* [Agent handoff targets](/agents/handoff-targets) — route a conversation to another agent instead of a human.
* [Cost controls](/agents/cost-controls) — cap what an agent can spend per run and per conversation.
