> ## Documentation Index
> Fetch the complete documentation index at: https://docs.omneo.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Walk-in queues and waitlists

> Handle demand a normal booking cannot: check customers into a walk-in queue, register interest on a waitlist, and convert either into an appointment.

Use a walk-in queue when customers arrive without a booking, and a waitlist when no slot suits the customer now. Both are enabled per definition and both can be resolved by creating an appointment and linking it back. For the model behind them, see the [Appointments concept](/concepts/visits/appointments).

All endpoints are bearer authenticated and live under `https://api.[tenant].getomneo.com/api/v3`.

| Flow          | Enabled by                                     | Use when                                                                              |
| ------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------- |
| Walk-in queue | `allow_queue` (and `walk_in_only` definitions) | The customer is at the location now and wants to be seen in order of arrival.         |
| Waitlist      | `allow_waitlist`                               | No suitable slot is available, and the customer wants to be contacted when one opens. |

## Walk-in queue

Check a customer into the queue for a definition that allows walk-ins. Send a `POST` to `/appointment-queues` with the definition and location. Omit `profile_id` for an anonymous walk-in.

```shell theme={null}
curl -X POST "https://api.[tenant].getomneo.com/api/v3/appointment-queues" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "appointment_definition_id": 1,
    "location_id": 13,
    "profile_id": "a1b460b2-953f-4587-b4eb-fb0f29b55e02",
    "notes": "Walk-in, wants a fitting"
  }'
```

The response is `201`. For an anonymous walk-in, drop `profile_id`:

```shell theme={null}
curl -X POST "https://api.[tenant].getomneo.com/api/v3/appointment-queues" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{ "appointment_definition_id": 1, "location_id": 13 }'
```

The entry returns wrapped in a `data` object, with `status` defaulting to `waiting` and `appointment_id` still `null`:

```json theme={null}
{
  "data": {
    "id": 812,
    "appointment_definition_id": 1,
    "profile_id": "a1b460b2-953f-4587-b4eb-fb0f29b55e02",
    "profile": {
      "id": "a1b460b2-953f-4587-b4eb-fb0f29b55e02",
      "first_name": "Ada",
      "last_name": "Lovelace",
      "email": "ada@example.com"
    },
    "location_id": 13,
    "location": {
      "id": 13,
      "name": "Melbourne Central",
      "handle": "melbourne-central",
      "timezone": "Australia/Melbourne"
    },
    "staff_id": null,
    "staff": null,
    "appointment_id": null,
    "status": "waiting",
    "notes": "Walk-in, wants a fitting",
    "meta": {},
    "created_at": "2026-05-11T00:05:00Z",
    "updated_at": "2026-05-11T00:05:00Z"
  }
}
```

For an anonymous walk-in, `profile_id`, `profile`, `staff_id`, and `staff` are `null`.

| Field                       | Type            | Notes                                                                                   |
| --------------------------- | --------------- | --------------------------------------------------------------------------------------- |
| `appointment_definition_id` | integer         | Required. The definition the walk-in is for.                                            |
| `location_id`               | integer         | Required. Where the customer checked in.                                                |
| `profile_id`                | string or null  | Profile UUID. Omit for an anonymous walk-in.                                            |
| `staff_id`                  | string or null  | Assign a staff member to the entry.                                                     |
| `status`                    | enum            | `waiting`, `called`, `served`, or `cancelled`. Defaults to `waiting`.                   |
| `appointment_id`            | integer or null | The linked appointment, set when converting a served entry.                             |
| `notes`                     | string or null  | Free-text note.                                                                         |
| `meta`                      | object          | Custom key/value data.                                                                  |
| `id`                        | integer         | Response only. The queue entry's id.                                                    |
| `profile`                   | object or null  | Response only. `{ id, first_name, last_name, email }`, `null` for an anonymous walk-in. |
| `location`                  | object          | Response only. `{ id, name, handle, timezone }`.                                        |
| `staff`                     | object or null  | Response only. The assigned staff member, when one is set.                              |
| `created_at`, `updated_at`  | string          | Response only. UTC timestamps.                                                          |

A queue entry moves through four statuses.

```text theme={null}
waiting -> called -> served
        -> cancelled
```

| Status      | Meaning                                                                    |
| ----------- | -------------------------------------------------------------------------- |
| `waiting`   | The customer has checked in and is in line. This is the default on create. |
| `called`    | The customer has been called to be seen.                                   |
| `served`    | The customer has been seen.                                                |
| `cancelled` | The customer left the queue without being served.                          |

Advance the status with a `PUT` to `/appointment-queues/{id}`:

```shell theme={null}
curl -X PUT "https://api.[tenant].getomneo.com/api/v3/appointment-queues/{queueId}" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{ "status": "called" }'
```

### Converting a served entry into an appointment

To keep a record of the service, create an appointment and link it to the queue entry through the `appointment_id` field. Create the appointment first, then set `appointment_id` on the queue entry:

```shell theme={null}
curl -X PUT "https://api.[tenant].getomneo.com/api/v3/appointment-queues/{queueId}" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{ "status": "served", "appointment_id": 5012 }'
```

The queue entry now points at the appointment, so the walk-in and its resulting booking are tied together.

## Waitlist

Register a customer's interest when no slot suits. Send a `POST` to `/appointment-waitlists`. `profile_id` is required here (a waitlist entry always belongs to a known Profile). Include `desired_start_at` to record when the customer would like to be seen.

```shell theme={null}
curl -X POST "https://api.[tenant].getomneo.com/api/v3/appointment-waitlists" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "appointment_definition_id": 1,
    "profile_id": "a1b460b2-953f-4587-b4eb-fb0f29b55e02",
    "location_id": 13,
    "desired_start_at": "2026-05-18 10:00:00",
    "notes": "Prefers a morning slot"
  }'
```

The entry returns wrapped in a `data` object, with `status` defaulting to `active`:

```json theme={null}
{
  "data": {
    "id": 415,
    "appointment_definition_id": 1,
    "profile_id": "a1b460b2-953f-4587-b4eb-fb0f29b55e02",
    "profile": {
      "id": "a1b460b2-953f-4587-b4eb-fb0f29b55e02",
      "first_name": "Ada",
      "last_name": "Lovelace",
      "email": "ada@example.com"
    },
    "location_id": 13,
    "location": {
      "id": 13,
      "name": "Melbourne Central",
      "handle": "melbourne-central",
      "timezone": "Australia/Melbourne"
    },
    "appointment_id": null,
    "status": "active",
    "desired_start_at": "2026-05-18T10:00:00Z",
    "notes": "Prefers a morning slot",
    "meta": {},
    "created_at": "2026-05-11T00:10:00Z",
    "updated_at": "2026-05-11T00:10:00Z"
  }
}
```

| Field                       | Type            | Notes                                                         |
| --------------------------- | --------------- | ------------------------------------------------------------- |
| `appointment_definition_id` | integer         | Required. The definition the customer is waiting for.         |
| `profile_id`                | string          | Required. A waitlist entry always belongs to a known Profile. |
| `location_id`               | integer         | Where the customer wants to be seen.                          |
| `desired_start_at`          | string or null  | When the customer would like to be seen.                      |
| `status`                    | enum            | `active`, `fulfilled`, or `cancelled`. Defaults to `active`.  |
| `appointment_id`            | integer or null | The linked appointment, set when fulfilling the entry.        |
| `notes`                     | string or null  | Free-text note.                                               |
| `meta`                      | object          | Custom key/value data.                                        |
| `id`                        | integer         | Response only. The waitlist entry's id.                       |
| `profile`                   | object          | Response only. `{ id, first_name, last_name, email }`.        |
| `location`                  | object          | Response only. `{ id, name, handle, timezone }`.              |
| `created_at`, `updated_at`  | string          | Response only. UTC timestamps.                                |

A waitlist entry moves through three statuses.

```text theme={null}
active -> fulfilled
       -> cancelled
```

| Status      | Meaning                                                            |
| ----------- | ------------------------------------------------------------------ |
| `active`    | The customer is waiting for a slot. This is the default on create. |
| `fulfilled` | A slot opened and the entry became an appointment.                 |
| `cancelled` | The customer no longer wants a slot.                               |

### Fulfilling a waitlist entry

When a slot opens, create an appointment for the customer, then fulfil the entry by linking the appointment through `appointment_id` and setting the status to `fulfilled`:

```shell theme={null}
curl -X PUT "https://api.[tenant].getomneo.com/api/v3/appointment-waitlists/{waitlistId}" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{ "status": "fulfilled", "appointment_id": 5012 }'
```

## Filtering queues and waitlists

Both lists support the same filter pattern as the rest of the API. Filter by definition, location, and status to build an operator view for one service at one store.

```shell theme={null}
# Everyone waiting in the queue for one service at one location
GET /api/v3/appointment-queues?filter[appointment_definition_id]=1&filter[location_id]=13&filter[status]=waiting

# Active waitlist entries for one service
GET /api/v3/appointment-waitlists?filter[appointment_definition_id]=1&filter[status]=active
```

| Filter                      | Applies to                                                                                       | Example                               |
| --------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------- |
| `appointment_definition_id` | Both                                                                                             | `filter[appointment_definition_id]=1` |
| `location_id`               | Both                                                                                             | `filter[location_id]=13`              |
| `status`                    | Queue: `waiting`, `called`, `served`, `cancelled`. Waitlist: `active`, `fulfilled`, `cancelled`. | `filter[status]=waiting`              |
| `profile_id`                | Both                                                                                             | `filter[profile_id]={profileId}`      |

<Note>Both list endpoints are paginated. Use `page[size]` and page through `meta.pagination`, the same as every other Omneo list endpoint.</Note>

## End-to-end example: a walk-in becomes a booked service

This is the full arc for a customer who arrives without a booking and is served.

<Steps>
  <Step title="Check the customer in">
    ```shell theme={null}
    curl -X POST "https://api.[tenant].getomneo.com/api/v3/appointment-queues" \
      -H "Authorization: Bearer ${TOKEN}" \
      -H "Content-Type: application/json" \
      -d '{ "appointment_definition_id": 1, "location_id": 13, "profile_id": "a1b460b2-953f-4587-b4eb-fb0f29b55e02" }'
    ```

    The entry is created with `status: "waiting"` and an `id` (here `812`).
  </Step>

  <Step title="Call the customer">
    ```shell theme={null}
    curl -X PUT "https://api.[tenant].getomneo.com/api/v3/appointment-queues/812" \
      -H "Authorization: Bearer ${TOKEN}" \
      -H "Content-Type: application/json" \
      -d '{ "status": "called" }'
    ```
  </Step>

  <Step title="Create the appointment for the service">
    ```shell theme={null}
    curl -X POST "https://api.[tenant].getomneo.com/api/v3/appointments" \
      -H "Authorization: Bearer ${TOKEN}" \
      -H "Content-Type: application/json" \
      -d '{
        "appointment_definition_id": 1,
        "profile_id": "a1b460b2-953f-4587-b4eb-fb0f29b55e02",
        "location_id": 13,
        "scheduled_start_at": "2026-05-11 10:15:00",
        "scheduled_end_at": "2026-05-11 10:45:00",
        "timezone": "Australia/Melbourne"
      }'
    ```

    Take the appointment `id` from the `201` response.
  </Step>

  <Step title="Mark served and link the appointment">
    ```shell theme={null}
    curl -X PUT "https://api.[tenant].getomneo.com/api/v3/appointment-queues/812" \
      -H "Authorization: Bearer ${TOKEN}" \
      -H "Content-Type: application/json" \
      -d '{ "status": "served", "appointment_id": 5012 }'
    ```

    The queue entry now points at the appointment, tying the walk-in to its booking. A waitlist entry follows the same shape: create the appointment, then `PUT` the entry with `status: "fulfilled"` and `appointment_id`.
  </Step>
</Steps>

## Response codes

| Method   | Endpoint                                        | Success | Errors                     |
| -------- | ----------------------------------------------- | ------- | -------------------------- |
| `POST`   | `/appointment-queues`, `/appointment-waitlists` | `201`   | `401`, `403`, `422`        |
| `GET`    | `.../{id}`                                      | `200`   | `401`, `403`, `404`        |
| `PUT`    | `.../{id}`                                      | `200`   | `401`, `403`, `404`, `422` |
| `DELETE` | `.../{id}`                                      | `204`   | `401`, `403`, `404`        |

## Errors

| Status | When                                                                                                                                    |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| `401`  | Missing or invalid bearer token.                                                                                                        |
| `403`  | The token lacks the required scope, or the actor lacks permission.                                                                      |
| `404`  | The queue or waitlist entry does not exist.                                                                                             |
| `422`  | Validation failed, for example a missing `appointment_definition_id`, a waitlist create without a `profile_id`, or an invalid `status`. |

## Edge cases and gotchas

* **Anonymous walk-ins have no Profile.** Omit `profile_id` on a queue create for a walk-in you cannot identify. The waitlist always requires `profile_id`, because an entry has to belong to a known customer so you can contact them.
* **Linking is the source of truth, not conversion.** Nothing converts a queue or waitlist entry automatically. You create the appointment yourself, then set `appointment_id` on the entry and move its `status`.
* **Statuses differ between the two.** A queue moves through `waiting`, `called`, `served`, `cancelled`; a waitlist through `active`, `fulfilled`, `cancelled`. Filter with the values that belong to each resource.
* **Deleting removes the entry.** `DELETE` returns `204` and no body. To keep the record, set `status` to `cancelled` instead.

## Related

* [Appointments concept](/concepts/visits/appointments)
* [Working with appointments](/dev-guides/visits/appointments)
* [Managing appointment definitions](/dev-guides/visits/appointment-definitions)
* [Add appointment queue API](/api-reference/appointment-queue/add-appointment-queue)
* [Add appointment waitlist API](/api-reference/appointment-waitlist/add-appointment-waitlist)
* [Add appointment API](/api-reference/appointment/add-appointment)
