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

# Working with Appointments

> Booking appointments against a definition, fetching a customer's appointments, and managing the lifecycle via the Omneo API.

To let a customer book a service, you read a published Appointment Definition, fetch its availability for a location and date, then create an appointment against the chosen slot. This guide covers that flow and the queries most front ends need. For the model behind it, see the [Appointments concept](/concepts/visits/appointments).

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

## Booking flow

<Steps>
  <Step title="List published definitions">
    ```shell theme={null}
    GET /api/v3/appointment-definitions?filter[is_published]=1&filter[is_archived]=0
    ```
  </Step>

  <Step title="Read the definition and its booking questions">
    ```shell theme={null}
    GET /api/v3/appointment-definitions/{definitionId}
    GET /api/v3/appointment-definitions/{definitionId}/questions
    ```

    The definition returns its normal and special hours, linked locations, staff, and whether it has a booking questionnaire. The questions endpoint returns the active form slots to render.
  </Step>

  <Step title="Fetch availability for a location and date">
    ```shell theme={null}
    curl -X POST "https://api.[tenant].getomneo.com/api/v3/appointment-definitions/{definitionId}/available-slots" \
      -H "Authorization: Bearer ${TOKEN}" \
      -H "Content-Type: application/json" \
      -d '{
        "location_id": 13,
        "date": "2026-05-11"
      }'
    ```

    When the definition has `requires_staff: true`, pass `staff_id`, and first call `available-staff` to populate a staff picker. For a calendar view, use `available-slots-range` with `start_date` and `end_date` (capped at 31 days) instead of calling the single-day endpoint repeatedly.
  </Step>

  <Step title="Create the appointment">
    ```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:00:00",
        "scheduled_end_at": "2026-05-11 10:30:00",
        "timezone": "Australia/Melbourne",
        "answers": [
          { "questionnaire_question_id": 1, "value": "consultation" }
        ]
      }'
    ```

    For an `instant` definition the response status is `confirmed`. For an `approval_required` definition it is `requested`. Times are sent in `timezone` and stored in UTC. Pass `assigned_staff_id` when the definition requires staff.
  </Step>
</Steps>

The create response includes the saved `answers[]`, so you can confirm what was captured without a follow-up read.

## Fetching a customer's appointments

Use the profile-scoped endpoint to list appointments for a specific profile:

```shell theme={null}
GET /api/v3/profiles/{profileId}/appointments
```

The global endpoint is also available and accepts a profile filter, which is useful for admin queries that combine filters across multiple profiles or need additional filtering dimensions not exposed on the profile-scoped route:

```shell theme={null}
GET /api/v3/appointments?filter[profile_id]={profileId}
```

The appointments list is filterable by every stored field, so you can narrow a customer's appointments without filtering on the front end:

| Filter                      | Example                               | Use                                                                                          |
| --------------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------- |
| `profile_id`                | `filter[profile_id]={profileId}`      | All appointments for one customer.                                                           |
| `status`                    | `filter[status]=confirmed`            | Only confirmed (or `requested`, `arrived`, `completed`, `cancelled`, `no_show`, `rejected`). |
| `appointment_definition_id` | `filter[appointment_definition_id]=1` | Appointments for one service.                                                                |
| `location_id`               | `filter[location_id]=13`              | Appointments at one location.                                                                |
| `assigned_staff_id`         | `filter[assigned_staff_id]={staffId}` | Appointments for one staff member.                                                           |

Combine filters with AND logic. For a customer's upcoming confirmed bookings:

```shell theme={null}
GET /api/v3/appointments?filter[profile_id]={profileId}&filter[status]=confirmed
```

<Note>The list endpoint is paginated. Use `page[size]` to control the page size and page through `meta.pagination` like every other Omneo list endpoint.</Note>

## Listing and filtering definitions

The Appointment Definition list supports these filters:

| Filter                                                                                                                     | Notes                                              |
| -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| `id`, `handle`, `name`                                                                                                     | Exact match.                                       |
| `is_published`, `is_archived`                                                                                              | Publication state.                                 |
| `booking_type`                                                                                                             | `instant`, `approval_required`, or `walk_in_only`. |
| `allow_customer_booking`, `allow_walk_in`, `requires_staff`, `customer_must_select_staff`, `allow_waitlist`, `allow_queue` | Boolean capability flags.                          |
| `queue_code`                                                                                                               | Walk-in queue code.                                |
| `definitionLocations.location_id`, `definitionLocations.is_active`                                                         | Definitions offered at a location.                 |
| `created_at`, `updated_at`                                                                                                 | Timestamps.                                        |

For example, every published definition bookable at a location:

```shell theme={null}
GET /api/v3/appointment-definitions?filter[is_published]=1&filter[definitionLocations.location_id]=13
```

### Filtering appointments by type

The Appointment Definition is the type. To fetch every appointment of a given type, filter the appointments list by its definition rather than fetching everything and filtering on the front end:

```shell theme={null}
# All appointments for one definition (one "type" of service)
GET /api/v3/appointments?filter[appointment_definition_id]=1

# Narrow to one customer and one type
GET /api/v3/appointments?filter[profile_id]={profileId}&filter[appointment_definition_id]=1
```

This is the same Definition/Instance filter pattern used elsewhere in the API.

<Warning>
  The appointments list filters by definition **id** only. It does not currently accept the definition `handle` as a relational filter (`filter[appointmentDefinition.handle]` is ignored), unlike some other instance endpoints such as Credits, which accept `filter[definition.handle]`. If you only have a handle, resolve it to an id first:

  ```shell theme={null}
  GET /api/v3/appointment-definitions?filter[handle]=bra-fitting
  ```

  Then use the returned `id` in `filter[appointment_definition_id]`. Adding `appointmentDefinition.handle` to the appointments filter whitelist is a reasonable enhancement to request from the Omneo team.
</Warning>

There is no separate category or `type` field on a definition, and `meta` is not filterable, so the definition id is the way to group appointments by type.

## Managing the lifecycle

Update an appointment with `PUT /api/v3/appointments/{id}`. Sending a `status` sets the matching timestamp automatically.

```shell theme={null}
# Approve a requested appointment
curl -X PUT "https://api.[tenant].getomneo.com/api/v3/appointments/{id}" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{ "status": "confirmed" }'
```

| Action                              | Body                                                                            |
| ----------------------------------- | ------------------------------------------------------------------------------- |
| Approve                             | `{ "status": "confirmed" }`                                                     |
| Reject                              | `{ "status": "rejected" }`                                                      |
| Cancel                              | `{ "status": "cancelled" }`                                                     |
| Mark arrived, completed, or no-show | `{ "status": "arrived" }` (or `completed`, `no_show`)                           |
| Reschedule                          | `{ "scheduled_start_at": "...", "scheduled_end_at": "...", "timezone": "..." }` |

Booking answers are captured at creation only. To collect post-visit feedback, create a separate questionnaire and submit it through `POST /api/v3/questionnaires/{id}/submissions`.

## Walk-in queues and waitlists

For `walk_in_only` definitions, check a customer in to the queue. Omit `profile_id` for an anonymous walk-in.

```shell theme={null}
POST /api/v3/appointment-queues
```

When a slot is unavailable and the definition allows it, add the customer to the waitlist, then fulfil it later by creating an appointment and linking it.

```shell theme={null}
POST /api/v3/appointment-waitlists
```

## Related

* [Appointments concept](/concepts/visits/appointments)
* [Working with visits](/dev-guides/visits/working-with-visits)
* [Browse appointments API](/api-reference/appointment/browse-appointments)
* [Add appointment API](/api-reference/appointment/add-appointment)
* [Browse profile appointments API](/api-reference/profile-appointment/browse-profile-appointments)
* [Browse available appointment slots API](/api-reference/appointment-availability/browse-available-appointment-slots)
* [Browse available appointment slots over a date range API](/api-reference/appointment-availability/browse-available-appointment-slots-over-a-date-range)
* [Submit questionnaire API](/api-reference/questionnaire-submission/submit-questionnaire)
* [Profile Normal Hour API](/api-reference/profile-normal-hour/browse-profile-normal-hours)
* [Profile Special Hour API](/api-reference/profile-special-hour/browse-profile-special-hours)
