Troubleshooting: hot-desking sign-in rejects
Hot-desking binds one user to one shared desk phone for the span of a shift: sign-in attaches the agent’s extension to the device, sign-out returns the phone to the shared pool, and the session ledger records how every binding ended. When the API rejects a sign-in, sign-out, or admin release, the error code names exactly which session state won that race. This page walks the 409/410/404 decision tree and ends with the self-serve reset that unsticks a device without waiting on a ticket. Every reject below is a ledger-state guard — the desk phone never drops a call it is not bound to, and nothing here touches the device’s own SIP registration.Code matrix — which session state fired
Find the error code first, then jump to its section below.Concurrent race — HOT_DESK_RACE
409 HOT_DESK_RACE fires when two sign-ins collide for the same device or
the same user and yours lost. Enforcement is symmetric — one active session
per device, one per user — so a pair of simultaneous attempts can never
both succeed; the winner holds the device and the loser is asked to retry.
Fix it by retrying once and reading the board:
- Back off and retry once. A single retry after a short pause resolves the normal two-fingers-same-moment case.
- Check the board when the reject persists.
GET /api/v1/voice/hot-desking(active sessions, newest first, capped at 200) shows who currently holds the device — a stewarded retry loops only when someone else genuinely won. - Do not retry-loop a contested device. The 409 names the loser, not a fault you can flush; resolve who should hold the desk, then act.
Terminal rows — HOT_DESK_ALREADY_SIGNED_OUT
410 HOT_DESK_ALREADY_SIGNED_OUT fires on a sign-out when your session
already ended — by an earlier sign-out, an end-of-shift expiry, a
displacement, or an admin revoke. Sign-out is idempotent by design: the
second call reports when the session actually ended rather than erroring,
and a driver that ignores the idempotent response can surface this stricter
410 while missing the prior release metadata the idempotent response already
carried.
Fix it by treating the row as already free, then re-reading the ground
truth:
- Flip the UI to signed-out first. Whatever ended the binding already released the device back to the shared inbound pool — the 410 is a report, not a failure to clear.
- Re-check
GET /api/v1/voice/hot-desking/me. It returns the calling user’s active session, ornullunderdatawhen nothing is bound. That read is the authoritative answer to “am I still signed in anywhere?” - Only escalate when the read disagrees. A 410 paired with an active
merow means your client is signing out of a stale session id; sync the client against themeresponse.
Nothing to sign out — HOT_DESK_NO_ACTIVE_SESSION
404 HOT_DESK_NO_ACTIVE_SESSION fires on sign-out when you were never
signed in at all — no session to end. It is the “404 you never sat down”
sibling of the 410 above: the device is in the shared pool and is not
waiting on you.
Fix it at the client: a sign-out button that fires with no active session
is working from stale state — call GET /api/v1/voice/hot-desking/me first
and render the button only when a session exists.
Admin release rejects — HOT_DESK_ALREADY_RELEASED / HOT_DESK_NOT_FOUND
Owner/admin revoke (POST /api/v1/voice/hot-desking/:id/revoke) clears a
specific session row by its id — the stuck-session page in the dashboard.
Two terminal-only rejects guard it:
410 HOT_DESK_ALREADY_RELEASED— the row exists but already carries a release stamp (signed_out,displaced_by_new_session,revoked, or expiry). Safe to treat as success — the desk is free.404 HOT_DESK_NOT_FOUND— no session exists under that id, usually because the id came from a list fetched before another release cleared it. Re-list (GET /api/v1/voice/hot-desking) and re-target.
Self-serve reset — unstick a device in two calls
When a desk looks stuck — an agent left mid-shift, a 409 keeps landing, or the board shows a session that should be dead — this is the whole reset:- List the active sessions (
GET /api/v1/voice/hot-desking) and find the device’s row. Itsidand release fields name who is bound and how the binding last changed. - Revoke from the dashboard (open Voice → Hot-desking, find the
row, revoke) — or by API as owner/admin:
POST /api/v1/voice/hot-desking/:id/revoke. The row closes with release reasonrevokedand the phone returns to the shared pool. - Sign in fresh. The next sign-in binds cleanly — the revoke cleared the one-active-session-per-device guard that was rejecting it.
expiresAt on every sign-in so stuck sessions
self-clear before anyone reaches for this reset; the rotate-shift
worked example in the hot-desking guide shows
the full lifecycle.
What to capture before escalating
Work the reset above first. If a code keeps firing after a clean revoke and re-list — a 404 on an id the live list still shows, or a 409 whose board shows no contender — open a ticket with:- The full error body — code, message, and the
detailsobject. - The session id and the device SIP username involved.
- The row as the board shows it (
GET /api/v1/voice/hot-desking) when live and terminal answers disagree. - Your organization ID (Settings → Organization, or
organizationIdfromGET /api/v1/me).
What not to do
- Do not retry-loop a 409. The race names a loser; looped retries just hold the device for whoever keeps winning — read the board instead.
- Do not treat 410/404 as a crash. They are ledger-state guards; the phone’s own SIP registration and the shared inbound rule keep running through every one.
- Do not revoke to punish a race loser. Revoke clears stuck sessions; a contested desk resolves by deciding who holds it, not by force.
See also
- Voice → Hot-desking guide — the session lifecycle, reservation calendar, and no-show sweep this page triages.
- Voice API reference → Hot-Desking — the endpoint contracts (sign-in, sign-out, list, me, revoke).
- Error Code Reference — the catalog of all five
hot-desking codes plus the reservation-side
HOTELING_DOUBLE_BOOKED.