Skip to main content

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; 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); *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; 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 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: 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, conferences, delegation.

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).
  • Softphone — attaches the callSid resolved by a pull or pickup response to its registered contact; the softswitch completes the bridge.

See also