> ## 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.

# Agent hand-raise: request assistance on a live call

> Let an agent flag a supervisor for help without leaving the call. The request alerts every connected supervisor wallboard in under 100ms and pre-arms a whisper join. Covers the request, the open-hand list, and the resolve lifecycle.

# Agent Hand-Raise (Request Assistance)

When a caller escalates, an AI handoff stalls, or a decision needs a
manager's sign-off, the agent's fastest option used to be a message in a
separate chat tool, outside the call's context. Hand-raise replaces that
detour with one control on the call itself: the agent raises a hand, every
connected supervisor wallboard shows the request in under 100
milliseconds, and the supervisor can join as a whisper coach without
hunting for the call.

Hand-raise is Orbit's parity surface for Genesys Cloud's "Request
Assistance" button and Five9's "Help" button: a real-time wallboard alert
plus a pre-armed whisper control for the supervisor who answers it.

## How it differs from a supervisor-initiated whisper

A whisper can start from either side of the call:

* **Supervisor-initiated** — the supervisor watches the live monitoring
  wallboard and opens a whisper on their own judgement, covered in
  [whisper coaching](/guides/agent-assist-whisper-coaching).
* **Agent-initiated (hand-raise)** — the agent asks for help, and the
  request lands on the supervisor's wallboard carrying the call, the
  agent, and an optional reason, with a pre-armed "Join whisper" action.

In both shapes the supervisor still chooses what to do: whisper,
listen-only, full barge, or dismiss. Hand-raise changes who opens the
door, not who owns the decision.

One thing hand-raise never does: it does not dial anyone. A raised hand
creates a record and alerts the wallboard; the actual whisper join runs
over your tenant's existing voice path, exactly as a
supervisor-initiated whisper does. No new calling path is opened by this
feature.

## The two surfaces

### Agent: raise a hand — `POST /api/v1/voice/calls/:id/raise-hand`

The agent-side surface. The dashboard softphone's "Request Assistance"
control calls it on the active call, and any integration holding a voice
write scope for the agent can call it too. `:id` is the call identifier
the softphone reports for the live call; both outbound call ids and
inbound leg identifiers resolve. The agent identity is taken from the
authenticated session, so a client can raise a hand only for itself,
never for a colleague.

The body is optional:

| Field | Type | Notes |
| - | - | - |
| `reason` | string, max 240 characters | Free-form note shown on the supervisor tile, for example "caller disputing the invoice total". Omit it and the wallboard labels the request "Assistance requested". |

The response is the hand-raise row: `id`, `call_id`,
`agent_user_id`, `reason`, `status` (`"pending"` on create),
and `raised_at`.

Raising a hand is idempotent. A second press on the same call by the same
agent does not create a second alert: the request returns the existing
open row, and if the new press carries a clearer reason, that reason
replaces the old one on the supervisor tile. Expect this when an agent
double-clicks under stress; the wallboard simply keeps one tile.

If the call is not found in your tenant, the request returns 404.

### Supervisor: read the open-hand list — `GET /api/v1/voice/supervisor/hand-raises`

The supervisor-side surface, available to supervisor roles and above.
It returns the open-hand list, newest first, capped at 200 rows, plus a
counter object for a wallboard badge:

* `rows` — every hand-raise whose status is `pending` or
  `acknowledged`, each with `id`, `call_id`, `agent_user_id`,
  `reason`, `status`, and `raised_at`.
* `counts` — `{ pending, acknowledged, total }` for the pillar
  overview badge.
* `generated_at` — when the snapshot was taken.

Acknowledged rows stay in the list until they are resolved, on purpose:
the supervisor who acknowledged a request must be able to find the row
again when they are ready to join the call.

### Supervisor: close a request — `POST /api/v1/voice/supervisor/hand-raises/:id/resolve`

Closing the loop. The body records what the supervisor actually did:

| `supervisor_action` | Meaning |
| - | - |
| `whisper_joined` | Joined the call as a whispering coach |
| `listen_joined` | Joined listen-only |
| `barge_joined` | Joined fully audible to the caller |
| `dismissed` | Closed the request without joining |
| `call_ended` | The call ended on its own before anyone joined |

The row moves to `resolved` and stays in your tenant's records for
coaching and adherence analytics; hand-raises are never hard-deleted.
Resolving an already-resolved row is a no-op that returns the existing
row, so a double-click on "Join whisper" cannot produce duplicate audit
entries.

## Lifecycle of a hand-raise

Every hand-raise row moves through three statuses:

1. **pending** — created by `raise-hand`, visible on every supervisor
   wallboard, counted in `counts.pending`.
2. **acknowledged** — a supervisor has picked the request up. The row
   stays on the open list (and in `counts.acknowledged`) until it is
   resolved, so the acknowledging supervisor can return to it and join
   the call.
3. **resolved** — closed by `resolve` with the chosen
   `supervisor_action`, recorded with `resolved_at` and
   `resolved_by_user_id` for later review.

The open-hand rule is per call and per agent: one pending hand-raise per
(call, agent) pair at a time. That is what makes the action safe to
double-click, and it also means two agents on the same conference leg
can each raise their own hand.

## The wallboard alert path

On create, the API publishes a `voice.hand_raise.requested` event on the
capacity event stream (`GET /api/v1/voice/capacity/events`), the same
server-sent-events channel your wallboard already consumes for live
state changes. Connected supervisors see the alert in under 100ms with
no polling. The event payload carries everything a wallboard tile needs:

* `hand_raise_id`, `call_id`, `agent_user_id`, `raised_at`
* `reason`, when the agent sent one
* `suggested_supervisor_action: "whisper"`, so the tile can render a
  single pre-armed "Join whisper" button bound to the call, with no
  extra round trip

When a row is resolved, `voice.hand_raise.resolved` is published so
every other connected wallboard decrements its pending counter at the
same moment. Polling `GET /api/v1/voice/supervisor/hand-raises` on a
fixed cadence remains the fallback for wallboards that are not
connected to the stream.

## From acknowledge to whisper join

The intended supervisor flow, in pseudocode:

```text theme={null}
on event voice.hand_raise.requested:
    render tile(call_id, agent_user_id,
                reason or "Assistance requested",
                button "Join whisper"   # pre-armed by suggested_supervisor_action)

on supervisor clicks acknowledge:
    mark row acknowledged   # row stays on the open list until resolved

on supervisor clicks "Join whisper":
    POST /voice/calls/{call_id}/whisper
        # the supervisor's own session joins as coach; existing voice path
    POST /voice/supervisor/hand-raises/{hand_raise_id}/resolve
        { "supervisor_action": "whisper_joined" }
```

The acknowledged row remaining visible between "acknowledge" and "join"
is the design, not a delay: supervisors juggle several floors at once,
and the row is where they come back to find the call.

## Examples

### Agent raises a hand with a reason

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -s -X POST "https://api.orbit.devotel.io/api/v1/voice/calls/call_9f2ab1c4/raise-hand" \
      -H "X-API-Key: dv_live_sk_your_key_here" \
      -H "Content-Type: application/json" \
      -d '{"reason":"Caller is disputing the invoice total and wants a manager sign-off before I can proceed with the adjustment."}'
    ```

    ```typescript Node.js theme={null}
    import { Orbit } from "@devotel-orbit/node";

    const orbit = new Orbit({ apiKey: process.env.ORBIT_API_KEY });

    const { data } = await orbit.request("POST", "/voice/calls/call_9f2ab1c4/raise-hand", {
      reason:
        "Caller is disputing the invoice total and wants a manager sign-off before I can proceed with the adjustment.",
    });
    console.log(data.status); // "pending"
    ```

    ```python Python theme={null}
    # pip install devotel-orbit-sdk
    from orbit_sdk import OrbitClient

    client = OrbitClient.from_env()

    result = client.request(
        "POST",
        "/api/v1/voice/calls/call_9f2ab1c4/raise-hand",
        json={
            "reason": "Caller is disputing the invoice total and wants a manager "
            "sign-off before I can proceed with the adjustment."
        },
    )
    print(result["data"]["status"])  # "pending"
    ```
  </CodeGroup>
</RequestExample>

The reason is free text capped at 240 characters, sized to fit the
wallboard tile without truncation. Keep it to what the supervisor needs
to decide, not a call summary.

### Supervisor poll loop (SSE fallback)

```typescript Node.js theme={null}
import { Orbit } from "@devotel-orbit/node";

const orbit = new Orbit({ apiKey: process.env.ORBIT_API_KEY });

// Prefer GET /voice/capacity/events when connected: it pushes the alert
// in under 100ms. Poll only as the disconnected fallback.
for (;;) {
  const { data } = await orbit.request("GET", "/voice/supervisor/hand-raises");
  for (const row of data.rows) {
    if (row.status === "pending") renderRaisedHand(row);
  }
  setBadge(data.counts); // { pending, acknowledged, total }
  await new Promise((resolve) => setTimeout(resolve, 5000));
}
```

### Acknowledge, join, resolve

```bash cURL theme={null}
# Close the request after coaching the agent through the whisper.
curl -s -X POST "https://api.orbit.devotel.io/api/v1/voice/supervisor/hand-raises/8f1d4c2a-9b3e-4f6a-8c2d-1e5f7a9b0c3d/resolve" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"supervisor_action":"whisper_joined"}'
```

## Limits

* `reason` is free text only, capped at 240 characters. There are no
  structured reason categories yet.
* The open-hand list returns at most 200 rows per tenant. A floor with
  more simultaneous raised hands needs staffing attention before it
  needs pagination.
* One pending hand-raise per call and agent; re-pressing refreshes the
  existing request instead of queueing another.
* There is no escalation policy for an unanswered hand-raise today. An
  unacknowledged request stays pending until a supervisor resolves it
  or the call ends; automatic re-alerting after a silence threshold is
  a follow-up area. If you need a timer-based safety net now, pair
  hand-raise with
  [queue SLA escalation policies](/voice/queue-sla-escalation-policies).
* Hand-raise creates no outbound call. Whisper, listen, and barge joins
  reuse your tenant's existing voice path, so this feature adds nothing
  to your telephony routing.

## Related

* [Voice supervisor plane model](/concepts/voice-supervisor-plane-model)
  — how the wallboard, live monitoring, and supervisor scopes fit
  together.
* [Whisper coaching](/guides/agent-assist-whisper-coaching) — the
  supervisor-side whisper flow the pre-armed button hands off to.
* [Supervisor coaching](/guides/supervisor-coaching) — the broader
  coaching workflow hand-raise feeds into.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.