> ## 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: hoteling reservation cancel and revoke rejections

> Resolve HOTELING_NOT_FOUND, HOTELING_FORBIDDEN, and HOTELING_NOT_CANCELLABLE — map each 404/403/410 on a desk-reservation cancel to the state or role that fired it and the fix you own.

# Troubleshooting: hoteling reservation cancel and revoke rejections

The **reservation calendar** books a specific desk phone for a future
window; **walk-up hot-desking** binds an agent to a phone right now. The
two surfaces share one desk pool but carry separate reject vocabularies:
reservation-lifecycle rejections are `HOTELING_*` codes and are this
page; sign-in / sign-out / revoke rejections are `HOT_DESK_*` codes and
live in [Hot-desking sign-in
rejects](https://docs.orbit.devotel.io/troubleshooting/hot-desking-sign-in-rejects.md). The booking-time
collision `HOTELING_DOUBLE_BOOKED` is covered in the [hot-desking
guide](https://docs.orbit.devotel.io/guides/voice-hot-desking.md).

Every reject below is a reservation-state or role guard your
organization owns — your roles, your calendar, your cancels. None of
them touches a live call or the desk phone's SIP registration.

## Code matrix — which state or role fired

Find the error code first, then jump to its section below.

| Error code | HTTP | Fired on | Go to |
| - | - | - | - |
| `HOTELING_NOT_FOUND` | 404 | Cancel: no reservation exists under that id | [No reservation under that id](#no-reservation-under-that-id) |
| `HOTELING_FORBIDDEN` | 403 | Cancel: the caller is neither the booker nor an owner/admin | [Role check](#role-check) |
| `HOTELING_NOT_CANCELLABLE` | 410 | Cancel: the reservation is already in a terminal state | [Terminal reservations](#terminal-reservations) |

## No reservation under that id — HOTELING\_NOT\_FOUND

`404 HOTELING_NOT_FOUND` fires on
`DELETE /api/v1/voice/hoteling/reservations/:id` when the id matches no
reservation in your organization — the id never existed here, or the
request is hitting a different workspace than the one it was booked
under. A look-up trap separate from the reject: the agenda defaults to
`?scope=me`, so a teammate's id must be found through Team scope
before you can re-target a cancel at it.

Fix it by re-listing before you re-target:

1. **List with team scope.** `GET /api/v1/voice/hoteling/reservations?scope=team`
   shows every reservation in the organization, including the ones your
   `me` list hides.
2. **Re-target a live id.** Cancel against the id exactly as that list
   returns it.
3. **If the team scope still 404s**, the id was never created under
   this organization — check that the request and the API key belong to
   the same workspace.

## Role check — HOTELING\_FORBIDDEN

`403 HOTELING_FORBIDDEN` fires when someone other than the booker
attempts a cancel. The rule is one line: **the booker cancels their own
reservation, an owner or admin cancels any, and everyone else 403s.**

Fix it by routing the cancel to the right actor:

1. **If the reservation is yours**, retry with the booker's own
   credential — a different member's key or session 403s no matter what
   it intends.
2. **If it is someone else's**, ask them to cancel it from their
   **Mine** agenda view, or have an owner/admin run the same `DELETE`,
   which succeeds for them.
3. **Check the holder before retrying**: the **Team** scope on
   **Voice → Hot-desking** names the booker of any row.

If this reject is frequent, your roster is routing cancels through
accounts that are neither bookers nor admins — adjust member roles in
your organization settings, which is entirely tenant-owned.

## Terminal reservations — HOTELING\_NOT\_CANCELLABLE

`410 HOTELING_NOT_CANCELLABLE` fires when the reservation already
ended. A reservation ends in exactly four ways:

* `fulfilled` — the booker signed in during the window; the desk went
  to their session.
* `cancelled` — an earlier cancel won; this call is the second tap.
* `expired` — the window fully elapsed; the elapsed sweep flips these,
  and owner/admin can force it now with the sweep endpoint.
* released as a **no-show** — the booker never signed in and the
  15-minute grace ran out.

In all four there is nothing left to cancel: the desk is either held
by a session or back in the walk-up pool, and the 410 message names the
final state. The same 410 also settles a race where a cancel lands at
the same instant as another cancel or a sweep / no-show release —
exactly one of them wins.

Fix it by acting on the desk, not the old row:

1. **Treat the row as closed.** Re-read it with the team-scope list if
   you need the final state for your records.
2. **Book another window.** A fresh
   `POST /api/v1/voice/hoteling/reservations` is how the desk gets
   claimed again; a terminal reservation no longer holds it.
3. **Owner/admin reconciling the calendar**:
   `POST /api/v1/voice/hoteling/reservations/sweep` flips elapsed
   `reserved` rows to `expired` now and reports the released count —
   use it when the agenda shows windows that already passed.

## What to capture before escalating

Work the matrix above first. If a code keeps firing after a clean
team-scope re-list — a 404 on an id the live team list shows, or a 410
on a row the list still shows as `reserved` — open a ticket with:

1. **The full error body** — code, message, and any `details` object.
2. **The reservation id and the device SIP username** involved.
3. **The row as the team agenda shows it** when the live list and the
   reject disagree.
4. **Your organization ID** (Settings → Organization, or
   `organizationId` from `GET /api/v1/me`).

## What not to do

* **Do not retry-loop a 410.** Terminal is terminal; the desk decision
  is final, and looped cancels cannot reopen a fulfilled or expired
  window.
* **Do not treat a 403 as a broken credential.** The credential works;
  it simply is neither the booker's nor an owner/admin's.
* **Do not cancel from a stale list.** Re-list with `?scope=team`
  before any scripted cancel batch; most 404s are ids copied from an
  agenda fetched before a release ran.

## See also

* [Voice → Hot-desking guide](https://docs.orbit.devotel.io/guides/voice-hot-desking.md) — the
  reservation calendar, the 15-minute no-show grace, and the
  rotating-shift worked example this page triages.
* [Hot-desking sign-in rejects](https://docs.orbit.devotel.io/troubleshooting/hot-desking-sign-in-rejects.md)
  — the walk-up sibling: `HOT_DESK_RACE` and the terminal-session
  410/404 pair.
* [Voice API reference → Hot-Desking](https://docs.orbit.devotel.io/api-reference/voice.md#hot-desking)
  — the endpoint contracts (book, list, cancel, sweep).
* [Error Code Reference](https://docs.orbit.devotel.io/reference/error-codes.md) — the catalog,
  including the `HOTELING_*` reservation codes.


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