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.
The operations
| Operation | Request | Effect |
|---|---|---|
agenda.bookingOfferings.list |
GET /v1/agenda/booking-offerings |
Reads |
agenda.bookingLocations.list |
GET /v1/agenda/booking-locations |
Reads |
agenda.bookingRequirements.get |
GET /v1/agenda/booking-requirements |
Reads |
agenda.nearestBookingSlots.list |
GET /v1/agenda/nearest-booking-slots |
Reads |
agenda.bookingSlots.list |
GET /v1/agenda/booking-slots |
Reads |
agenda.bookings.create |
POST /v1/agenda/bookings |
Writes; idempotent |
agenda.bookings.lookup |
POST /v1/agenda/bookings/lookup |
Reads |
agenda.bookings.get |
GET /v1/agenda/bookings/{appointmentId} |
Reads |
agenda.bookings.reschedule |
POST /v1/agenda/bookings/{appointmentId}/reschedule |
Writes; not idempotent |
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 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 lists the sites, a page at a time.
Check the requirements
agenda.bookingRequirements.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. 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 |
bookingSlots.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 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 and 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 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.
Reschedule and cancel
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 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
slotTokentoagenda.bookings.reschedule. Another professional or room of that service is fine; a slot of another service is refused withSERVICE_CHANGED. Withagenda.bookingSlots.list, pass the appointment’s id asexcludeAppointmentIdso its current time doesn’t count as taken. - Cancel. Once the person confirms which appointment, send the
reasonthey gave toagenda.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 |
Writes; idempotent: repeating it with the same input has no further effect. | None |
agenda.bookings.reschedule |
Writes; not idempotent: a repeat may act again. | None |
agenda.bookings.cancel |
Deletes; idempotent. | None |
agenda.waitlist.join |
Writes; idempotent: the same patient, service and reason return the same open entry. | None |
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 or agenda.bookings.get before you repeat it. See Retries and 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:slipisnull. statusChangerecords the latest change of status: when, by whom and the reason given.- Staff change a status, such as to
confirmedorno_show, withagenda.appointments.changeStatus. It accepts only people, applies allowed transitions, and requires theexpectedVersionthey read.
Waitlist entries have their own statuses: see The waitlist.