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

# Troubleshooting: hot-desking sign-in rejects

> Resolve HOT_DESK_RACE, HOT_DESK_ALREADY_SIGNED_OUT, HOT_DESK_NO_ACTIVE_SESSION, HOT_DESK_ALREADY_RELEASED, and HOT_DESK_NOT_FOUND — map each 409/410/404 to the session state that fired it and the fix you own.

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

| Error code                    | HTTP | Fired on                                                            | Go to                                           |
| ----------------------------- | ---- | ------------------------------------------------------------------- | ----------------------------------------------- |
| `HOT_DESK_RACE`               | 409  | Sign-in: a concurrent sign-in for the same device or user won first | [Concurrent race](#concurrent-race)             |
| `HOT_DESK_ALREADY_SIGNED_OUT` | 410  | Sign-out: your session already ended                                | [Terminal rows](#terminal-rows)                 |
| `HOT_DESK_NO_ACTIVE_SESSION`  | 404  | Sign-out: you have no active session at all                         | [Nothing to sign out](#nothing-to-sign-out)     |
| `HOT_DESK_ALREADY_RELEASED`   | 410  | Admin release/revoke: that session was already released             | [Admin release rejects](#admin-release-rejects) |
| `HOT_DESK_NOT_FOUND`          | 404  | Admin release/revoke: no session exists under that id               | [Admin release rejects](#admin-release-rejects) |

## Concurrent race — HOT\_DESK\_RACE<a id="concurrent-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:

1. **Back off and retry once.** A single retry after a short pause resolves
   the normal two-fingers-same-moment case.
2. **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.
3. **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<a id="terminal-rows" />

`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:

1. **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.
2. **Re-check `GET /api/v1/voice/hot-desking/me`.** It returns the calling
   user's active session, or `null` under `data` when nothing is bound.
   That read is the authoritative answer to "am I still signed in anywhere?"
3. **Only escalate when the read disagrees.** A 410 paired with an active
   `me` row means your client is signing out of a stale session id; sync the
   client against the `me` response.

## Nothing to sign out — HOT\_DESK\_NO\_ACTIVE\_SESSION<a id="nothing-to-sign-out" />

`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<a id="admin-release-rejects" />

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:

1. **List the active sessions** (`GET /api/v1/voice/hot-desking`) and find
   the device's row. Its `id` and release fields name who is bound and how
   the binding last changed.
2. **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
   reason `revoked` and the phone returns to the shared pool.
3. **Sign in fresh.** The next sign-in binds cleanly — the revoke cleared
   the one-active-session-per-device guard that was rejecting it.

Set an end-of-shift `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](/guides/voice-hot-desking) 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:

1. **The full error body** — code, message, and the `details` object.
2. **The session id and the device SIP username** involved.
3. **The row as the board shows it** (`GET /api/v1/voice/hot-desking`)
   when live and terminal answers disagree.
4. **Your organization ID** (Settings → Organization, or `organizationId`
   from `GET /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](/guides/voice-hot-desking) — the session
  lifecycle, reservation calendar, and no-show sweep this page triages.
* [Voice API reference → Hot-Desking](/api-reference/voice#hot-desking) —
  the endpoint contracts (sign-in, sign-out, list, me, revoke).
* [Error Code Reference](/reference/error-codes) — the catalog of all five
  hot-desking codes plus the reservation-side `HOTELING_DOUBLE_BOOKED`.
