---
title: "Availability and booking"
description: "Find bookable times and book, reschedule or cancel appointments through Agenda's booking operations, with requirements, safe repeats and statuses."
---

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

# Availability and booking

The booking operations are how a system books for a patient: an AI agent on WhatsApp, a patient-facing app, a contact-centre tool. They find what the clinic offers and when, say what the clinic needs to know, book the time the patient chose, and later move or cancel it. People and machines (API keys and client credentials) may call every one of them. For the whole flow with curl, see [Book an appointment](/en/developers/guides/book-an-appointment/).

## The operations

| Operation | Request | Effect |
| --- | --- | --- |
| [`agenda.bookingOfferings.list`](/api/agenda/booking-offerings/list/) | `GET /v1/agenda/booking-offerings` | Reads |
| [`agenda.bookingLocations.list`](/api/agenda/booking-locations/list/) | `GET /v1/agenda/booking-locations` | Reads |
| [`agenda.bookingRequirements.get`](/api/agenda/booking-requirements/get/) | `GET /v1/agenda/booking-requirements` | Reads |
| [`agenda.nearestBookingSlots.list`](/api/agenda/nearest-booking-slots/list/) | `GET /v1/agenda/nearest-booking-slots` | Reads |
| [`agenda.bookingSlots.list`](/api/agenda/booking-slots/list/) | `GET /v1/agenda/booking-slots` | Reads |
| [`agenda.bookings.create`](/api/agenda/bookings/create/) | `POST /v1/agenda/bookings` | Writes; idempotent |
| [`agenda.bookings.lookup`](/api/agenda/bookings/lookup/) | `POST /v1/agenda/bookings/lookup` | Reads |
| [`agenda.bookings.get`](/api/agenda/bookings/get/) | `GET /v1/agenda/bookings/{appointmentId}` | Reads |
| [`agenda.bookings.reschedule`](/api/agenda/bookings/reschedule/) | `POST /v1/agenda/bookings/{appointmentId}/reschedule` | Writes; not idempotent |
| [`agenda.bookings.cancel`](/api/agenda/bookings/cancel/) | `POST /v1/agenda/bookings/{appointmentId}/cancel` | Deletes; idempotent |

Reads accept `agenda:bookings:read` or `agenda:bookings:write`; the three writes need `agenda:bookings:write`. Each operation also accepts `channel` and `connectionId`: the platform sets them and they change nothing in Agenda, so leave them out.

## Find what to book

[`agenda.bookingOfferings.list`](/api/agenda/booking-offerings/list/) finds active bookable offerings by `q` (a service's name, such as `neurología`), `serviceId` or `locationId`, 10 per page by default and at most 20. Search for the service the patient named rather than loading the whole catalog. Each offering carries what to check before you offer a time:

| Field | Meaning |
| --- | --- |
| `id`, `serviceId` | The offering and its service. |
| `durationMinutes`, `occupationMinutes` | How long the appointment lasts, and how long the patient stays when that's longer, such as a 12-hour study (otherwise `null`). |
| `agendas`, `locations` | The resources that provide it, and their sites, with the address to give the patient. |
| `scheduleSummary` | Its hours and closures in words. Availability is what counts. |
| `service.eligibility` | The ages the service takes, `minAgeYears` and `maxAgeYears`, in completed years on the appointment day; `null` for any age. |
| `service.bookingChecklist` | Requirements to compare with what you know about the patient before you offer a slot. |
| `service.patientInstructions` | Preparation and arrival instructions to explain to the patient. |

When exactly one site applies, use it without asking the patient to choose. A listed service doesn't establish that the patient's payer covers it. [`agenda.bookingLocations.list`](/api/agenda/booking-locations/list/) lists the sites, a page at a time.

## Check the requirements

[`agenda.bookingRequirements.get`](/api/agenda/booking-requirements/get/) answers what the organization needs to book:

| Field | What it says |
| --- | --- |
| `fields` | How each built-in field is asked: `off`, `optional` or `required` for `phone`, `email`, `payer`, `authorization` and `notes`; `optional` or `required` for `document`; `on` or `off` for `priority`. |
| `customFields` | The organization's own fields: `key`, `label`, `type` (`text`, `number`, `date`, `select` or `checkbox`), `options` and `required`. |
| `documentTypes` | The document type codes it uses, such as `CC` or `TI`. |
| `payers` | Its active payers. Each has a `bookingPolicy`, `open` or `staff_only`, and `sites`, the sites (IPS or *sedes*) the clinic serves under that payer: a patient whose site isn't listed isn't served under it, and an empty list means no restriction. |

Pass `payerQuery` with the payer's name, as it appears on the patient's insurance card, to leave out unrelated payers; an empty match doesn't establish coverage. Only the clinic's team books the patients of a `staff_only` payer: don't book them, add them to the waitlist with the reason `payer_review` through [`agenda.waitlist.join`](/api/agenda/waitlist/join/). Reuse what the patient has already told you, and ask only for required values that are missing or unclear.

## Find a slot

Two operations answer slots, earliest first, each with a `slotToken`:

| | [`nearestBookingSlots.list`](/api/agenda/nearest-booking-slots/list/) | [`bookingSlots.list`](/api/agenda/booking-slots/list/) |
| --- | --- | --- |
| Searches | One service, across every agenda that offers it | One offering, from `from` up to `to` |
| Requires | `serviceId`, `timezone` | `offeringId`, `from`, `to`, `timezone` |
| Narrows or steers with | `locationId`, `resourceId`, `from`, `days` | `preferredResourceIds`, agendas to consider first |
| Answers | Up to `limit` slots: 20 by default, at most 50 | Pages of 25 by default, at most 100 |
| Continues | Call again with `from` set to `nextFrom` | `pageInfo.nextCursor` |

`timezone` is the IANA time zone used to read local dates and working hours, such as `America/Bogota`.

To quote the nearest time, call [`agenda.nearestBookingSlots.list`](/api/agenda/nearest-booking-slots/list/) with a `limit` of 1 to 3 and leave `days` out: it then searches as far ahead as the service can be booked, so an opening months away comes back in one call. Don't page week by week. `group: "offering"` answers each offering's first slots instead of the earliest overall. An empty `data` means there's no opening within that reach, and `searchedUntil` says how far it looked: offer the waitlist. `truncatedOfferings: true` means some agendas went unexamined; narrow the search with `locationId` or `resourceId`.

[`agenda.firstAvailability.get`](/api/agenda/first-availability/get/) and [`agenda.availability.list`](/api/agenda/availability/list/) answer the same kind of slots to credentials with `agenda:availability:read`. They also take `durationMinutes`, admitted only when the organization permits changing the duration, and `occupationMinutes`, a study length the offering offers.

## Book

Book only after the patient explicitly confirms the time. [`agenda.bookings.create`](/api/agenda/bookings/create/) takes the `slotToken` exactly as it was returned:

| Field | Meaning |
| --- | --- |
| `slotToken` | Required. The slot the patient confirmed. |
| `patient` | `name`, `documentNumber` and `documentType`, one of the organization's codes (the first one when left out). |
| `patientBirthDate` | `YYYY-MM-DD`, as the patient gives it. Needed when the service has an age rule. Never infer age from a name or a document type. |
| `intake` | `phone`, `email`, `entity` (the payer, as the clinic names it), `authorization`, `notes`, `priority` (`normal` or `urgent`), and `fields`, the custom fields by `key`. |

The booking also creates or updates the patient's record, whose id comes back as `patientId`. The answer is the appointment, with:

- `guidance`: the service's patient instructions, whether patients are seen in arrival order, who attends, and the site with its address. Tell the patient.
- `slip`: a link to the appointment's printable PDF slip, valid for an hour (`expiresAt`). Fetch it right after booking and send the patient the file, never the link as text: the link needs no credential.

The errors a booking can meet, and what to do about each, are in [Book an appointment](/en/developers/guides/book-an-appointment/#handle-the-common-errors).

## Reschedule and cancel

[`agenda.bookings.lookup`](/api/agenda/bookings/lookup/) finds a patient by the exact `documentNumber` (and `documentType`, when known) and answers their appointments, newest first, each with its `id`, `startsAt`, `status` and agendas; upcoming ones also carry `guidance`. `found: false` means no patient has that document. When several patients share the number under different types, `patients` lists them and `appointments` is empty: ask which one, and call again with `documentType`. [`agenda.bookings.get`](/api/agenda/bookings/get/) reads one appointment by id, with its guidance and slip.

- **Reschedule.** Find a new time for the same service, have the patient confirm it, and send its `slotToken` to [`agenda.bookings.reschedule`](/api/agenda/bookings/reschedule/). Another professional or room of that service is fine; a slot of another service is refused with `SERVICE_CHANGED`. With [`agenda.bookingSlots.list`](/api/agenda/booking-slots/list/), pass the appointment's id as `excludeAppointmentId` so its current time doesn't count as taken.
- **Cancel.** Once the person confirms which appointment, send the `reason` they gave to [`agenda.bookings.cancel`](/api/agenda/bookings/cancel/). The reason is kept in the appointment's history, and the slot is freed.

## Safe repeats

| Operation | Effect | Idempotency key |
| --- | --- | --- |
| [`agenda.bookings.create`](/api/agenda/bookings/create/) | Writes; idempotent: repeating it with the same input has no further effect. | None |
| [`agenda.bookings.reschedule`](/api/agenda/bookings/reschedule/) | Writes; not idempotent: a repeat may act again. | None |
| [`agenda.bookings.cancel`](/api/agenda/bookings/cancel/) | Deletes; idempotent. | None |
| [`agenda.waitlist.join`](/api/agenda/waitlist/join/) | Writes; idempotent: the same patient, service and reason return the same open entry. | None |
| [`agenda.appointments.create`](/api/agenda/appointments/create/) | Writes. People only. | `idempotencyKey`, required and unique within the organization: reusing it returns the original appointment. |

The booking operations take no idempotency key, so when a write's outcome is unknown, check with [`agenda.bookings.lookup`](/api/agenda/bookings/lookup/) or [`agenda.bookings.get`](/api/agenda/bookings/get/) before you repeat it. See [Retries](/en/developers/concepts/retries/) and [Handle errors and retries](/en/developers/guides/handle-errors-and-retries/).

## Statuses

An appointment's `status` is one of `tentative`, `scheduled`, `confirmed`, `checked_in`, `completed`, `cancelled`, `no_show` or `unknown`.

- Cancelling frees the slot. Cancelled and completed appointments don't regain capacity through a status change.
- A cancelled appointment can't be edited (`APPOINTMENT_CLOSED`), a closed one can't be moved (`APPOINTMENT_NOT_MOVABLE`), and a cancelled one has no slip: `slip` is `null`.
- `statusChange` records the latest change of status: when, by whom and the reason given.
- Staff change a status, such as to `confirmed` or `no_show`, with [`agenda.appointments.changeStatus`](/api/agenda/appointments/change-status/). It accepts only people, applies allowed transitions, and requires the `expectedVersion` they read.

Waitlist entries have their own statuses: see [The waitlist](/en/developers/agenda/#the-waitlist).

Source: https://docs.getarbol.com/en/developers/agenda/availability-and-booking/index.mdx
