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

# Call flip and call pickup: the handoff-token and ringing-registry models

> What flip and pickup each re-target, how the two-step pull token and the org-namespaced ringing registry work, one-shot claim arbitration, the DTMF parity codes, and how flip, pickup, park, conference, SLA mirroring, and delegation partition the handoff space.

# Call flip and call pickup: the handoff-token and ringing-registry models

**Call flip** moves a call you are already on from one of your devices to
another. **Call pickup** lets you answer a call that is still ringing on a
colleague's extension. Both re-target a leg that already exists in your
tenant through the softswitch control plane: neither originates a PSTN leg,
so neither touches your termination paths, caller id, or per-minute cost at
all.

This page defines the two models — the flip's two-step token handoff, the
pickup's ringing-leg registry, the one-shot claim arbitration they share,
the DTMF parity codes, and the border with park, conference, SLA mirroring,
and delegation — so the procedural guides stay thin. Read it before you
wire a client against `/api/v1/voice/call-flip` or
`/api/v1/voice/pickup`. The full `FLIP_…` / `PICKUP_…` code matrix lives in
the [flip and pickup troubleshooting
catalog](https://docs.orbit.devotel.io/troubleshooting/conference-flip-pickup.md); this page explains the
model those codes name.

## 1. One invariant, two verbs

* **Flip** re-targets an **answered** leg between **your own** devices: the
  remote party stays on the line while your end hops, say, from the desk
  phone to the mobile softphone.
* **Pickup** claims a **ringing**, pre-answer leg of a **colleague**: nobody
  has answered yet, and your device becomes the one that does.

The shared invariant is that the leg already exists. The softswitch
re-INVITEs the live or ringing leg to the pulling device's registered
contact; flip and pickup originate nothing toward the PSTN and therefore
have no termination-path failure modes. That is also the border between
the two: flip acts on your own call after answer, pickup acts on someone
else's call before answer.

## 2. Call flip: the two-step token handoff

Flip is a push-then-pull protocol between two of your devices, mediated by
a short-lived handoff token.

1. **Issue.** From the device holding the call,
   `POST /api/v1/voice/call-flip` with the live `callSid`. Optionally pin
   the pull to one device with `targetDeviceUsername`, and pass
   `remotePartyNumber` / `remotePartyName` for tray display. The response
   carries the handoff: the full `token`, a 4-digit `shortCode`, and an
   `expiresAt` about two minutes out.
2. **Pull.** From the destination device,
   `POST /api/v1/voice/call-flip/pull` with the `token` and the pulling
   device's `pulledByDeviceUsername`. The claim is atomic: exactly one of
   two simultaneous pulls wins, and the winner's response resolves the
   `callSid` so the soft client can attach its media path while the
   softswitch re-INVITEs the leg.

The two-minute expiry is the human "I am walking outside" budget: shorter
and the pull races the walk, longer and a forgotten token keeps
advertising a call nobody is moving. Inside the window:

* **Re-issue is idempotent.** Issuing again for the same call as the same
  user while a live token exists returns the existing handoff instead of
  stacking a second token.
* **Losers learn why.** A pull on a consumed token returns
  `FLIP_ALREADY_PULLED` with a `details.prior` view naming the device that
  won; on a stale one, `FLIP_EXPIRED`; from a device other than the pinned
  target, `FLIP_TARGET_MISMATCH` naming the expected device. A loser
  re-lists, it does not blind-retry.
* **The issuer can change their mind.**
  `POST /api/v1/voice/call-flip/:id/cancel` retires an unused token; a
  later pull reads it as gone. Owners, admins, and supervisors may cancel
  any flip in the tenant — the recovery path when an agent walked away —
  while everyone else cancels only their own.
* **The list never leaks the token.** `GET /api/v1/voice/call-flip`
  returns the tenant's live handoffs, newest first, for a pending-flips
  tray. The full token exists only in the issue response to the issuing
  user, and never re-appears on any read.

### The desk-phone path: `*7` plus the short code

A registered device that renders no UI pulls by DTMF instead: dial `*7`
then the 4-digit short code. The IVR resolves the code against your tenant
through an authenticated internal endpoint and runs the same atomic claim
as the HTTP pull — the dial path is a second door onto one handoff, not a
second model.

Keep the digit ranges straight: `*1` through `*9` retrieve **park** slots
([the slot aliasing model](https://docs.orbit.devotel.io/concepts/voice-call-park-lobby.md)); `*7` plus a
4-digit code pulls a **flip**. Different registries, different verbs.

## 3. Call pickup: the ringing-leg registry

Pickup's claimable set is the org's ringing registry: an org-namespaced
sorted set holding exactly the pre-answer legs, each scored by when it
started ringing so oldest-first is the native read order. It is
deliberately not the active-call ledger, which records answered legs only
and therefore can never answer "what is ringing on 201 right now." Ringing
is sub-30-second ephemeral state with a natural expiry: entries self-evict
shortly after the worst-case ring timeout even when a terminal webhook
drops, so a phantom ring can never sit in the tray. The softswitch's
ringing and terminal webhooks feed the registry through authenticated
internal endpoints; nothing you code against writes to it directly.

Three calls read and claim it:

* **List.** `GET /api/v1/voice/pickup` returns every claimable ringing
  call, oldest first: the extension and username it rings on, the caller's
  number and name, and ringing-since. That is the pickup tray.
* **Directed pickup.** `POST /api/v1/voice/pickup/directed` with
  `targetExtension` and your `pickingUpDeviceUsername` — answer the call
  ringing on 201, the API form of the desk phone's `**201`. When an
  extension has several calls ringing at once (hunt-group fan-out), the
  oldest is the one claimed.
* **Group pickup.** `POST /api/v1/voice/pickup/group` with your
  `pickingUpDeviceUsername` — answer the oldest ringing call in your
  pickup group, the API form of `*8`. Pass `groupMembers` to scope the
  claim to a named set of extensions, the same membership shape the floor
  already uses for [team chat](https://docs.orbit.devotel.io/concepts/team-chat-model.md); omit it and the
  whole organization is the default pickup group.

Both claim paths are resolve-then-claim: a resolve step names the
candidate (that extension's ring, or the group's oldest), then an atomic
claim removes it from the registry in one move. Two colleagues hitting the
same ring in the same instant produce exactly one winner; the loser
receives `PICKUP_ALREADY_TAKEN` and re-lists. When nothing matches, the
answer is `PICKUP_NO_RINGING_CALL`, and the discipline is the same: re-list
with `GET /api/v1/voice/pickup` rather than retrying the claim. A tray miss
means the call was answered, taken first, or rang out — only a fresh tray
tells you which.

Two limits stated plainly: pickup reaches **still-ringing inbound legs**
only — an answered call is transfer or flip territory — and the registry
is a live cache of ringing, not a call record.

## 4. One-shot claims, and reading them in analytics

Flip and pickup share one arbitration shape: a resolve step that names a
candidate, then a single atomic claim that can succeed exactly once across
the cluster. One bridge, one audit entry, one winning device; every
contested outcome (already-pulled, expired, already-taken) is the ledger
telling the loser what the winner did.

That one-shot nature is what makes the analytics reading clean. The
recurring conference-floor pattern — a participant flipped out of a live
room into a directed 1:1 hand-off — emits `participant_left` while the
room continues ([the conference
model](https://docs.orbit.devotel.io/concepts/voice-conference-model.md) names the pattern). Count it as a
directed hand-off, never as a room or leg failure: the leg did not fail,
it moved. Symmetrically, a ringing entry that vanishes while an answered
leg appears on another device is a pickup, not a missed call.

## 5. Partitioning the handoff space

Six features partition "move a call" by trigger state and by who may act:

| Feature | Trigger state | Who may act | Device and extension semantics |
| - | - | - | - |
| **Flip** | Answered, your leg | You, or the one device you pinned | Between your own registered devices; token pull or `*7` + short code |
| **Pickup** | Ringing, a colleague's leg | Any colleague in the pickup group (default: the whole org) | Directed at a named extension (`**201` parity) or oldest-in-group (`*8` parity) |
| **Park** | Active, your leg | Anyone on the floor, once parked | Your own call stashed in shared slots 1–9; `*1`–`*9` retrieve |
| **Conference** | Any legs | The room's bridge controls | Bridges several legs into one mix |
| **SLA mirroring** | Ringing, one line | The roster configured for the line | Mirrors one line's ring onto a roster; the call is not moved, it rings everywhere |
| **Delegation** | Per the owner's grant | The named delegate | Owner-grants-delegate scope over the owner's calls |

Read the rows as exclusive choices over the same floor: park publishes
**your own active** call for anyone to retrieve later; pickup claims
**someone else's ringing** call right now; flip follows **your own
answered** call to another of your devices. A park lobby survives for
minutes; a pickup entry exists only as long as the ring; a flip token
lives about two minutes. The neighboring guides: [park
lobby](https://docs.orbit.devotel.io/guides/voice-call-park-lobby.md),
[conferences](https://docs.orbit.devotel.io/voice/conferences.md),
[delegation](https://docs.orbit.devotel.io/guides/call-delegation-executive-assistant.md).

## 6. Surface map

* **Dashboard active-call view** — the flip action on a live call issues
  the token; the pending-flips tray reads the list endpoint.
* **Desk phones** — `*7` + short code pulls a flip; `**` + extension is
  directed pickup; `*8` is group pickup.
* **Attendant console** — the pickup tray lists ringing calls and the
  console's pickup actions drive directed pickup ([attendant
  console](https://docs.orbit.devotel.io/voice/attendant-console.md)).
* **Softphone** — attaches the `callSid` resolved by a pull or pickup
  response to its registered contact; the softswitch completes the bridge.

## See also

* [Call flip and pickup troubleshooting](https://docs.orbit.devotel.io/troubleshooting/conference-flip-pickup.md) — the full `FLIP_…` / `PICKUP_…` / `CONF_…` matrix and claim-race diagnosis
* [Call park and the lobby](https://docs.orbit.devotel.io/concepts/voice-call-park-lobby.md) — the slot aliasing model for your own active calls
* [The conference model](https://docs.orbit.devotel.io/concepts/voice-conference-model.md) — multi-leg bridging, and the flip-pickup analytics pattern
* [Conferences](https://docs.orbit.devotel.io/voice/conferences.md) — the conference operating surface
* [Attendant console](https://docs.orbit.devotel.io/voice/attendant-console.md) — the operator tray that surfaces pickup
* [The team chat model](https://docs.orbit.devotel.io/concepts/team-chat-model.md) — the membership shape named pickup groups draw on
* [Call delegation for executive assistants](https://docs.orbit.devotel.io/guides/call-delegation-executive-assistant.md) — owner-grants-delegate scope


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