What you can do
Read the catalog
Branches, services, and professionals, with the rules that tie them together.
Query availability
Open slots for a given date, a range of dates, or simply the next ones available.
Book
Create an appointment with the customer’s data embedded — no prior customer record needed.
Confirm or cancel
Move an appointment out of
pending, or release the slot.Scopes
A public site that only displays branches, services and open times needs the four
:read scopes and nothing more. Add the appointment scopes only to the token that actually books.
The booking model
A branch is a location. It offers services, each with a duration and its own rules about which professionals can deliver it. Availability is computed from that combination, so a slot is always specific to a service — asking “what is free on Tuesday” without naming a service has no answer. The customer is embedded in the appointment, not referenced. You do not create a customer record first; you pass name, and optionally phone and email, inside the booking. That also means scheduling customers are separate from CRM customers — the two are not linked automatically.Appointments land in pending
Creating an appointment does not confirm it. It lands in pending and stays there until you call confirm explicitly.
That extra step exists so you can build flows where something has to happen in between — a payment clears, a human validates, or the customer confirms from a link. If your flow has none of that, chain create and confirm in the same request handler.
Confirm only accepts an appointment that is currently pending; anything else returns 409.
Availability is a snapshot, not a hold
Reading a slot does not reserve it. Between your availability call and your create call, someone else can take it — and create returns409 SLOT_NOT_AVAILABLE.
Design for it rather than around it: on 409, re-query availability and show the user the new options. Do not retry the same slot, and do not pre-fetch a whole day’s availability and treat it as valid minutes later.
The window matters most in the flows where a human is deciding in between. If your funnel has the customer picking a time and then filling a form, re-check availability on submit.
An appointment left pending is a slot held hostage
pending occupies the slot but is not a booking. Nothing expires it automatically.
If your flow can be abandoned — a payment that never clears, a form the customer walks away from — you need to cancel it yourself. A job that sweeps your own pending records older than your abandonment threshold is the usual answer. Without it, a busy day slowly fills with slots nobody can book and nobody is attending.
Tenancy is implicit
Branches, services, professionals, and appointments all belong to the company that owns your token. You never sendcompany, and sending it returns 400.
Reads cover the whole company: a token minted for one project sees every branch your tenant operates. Filter client-side if you need a subset.
Ids and time
The appointment_id is a UUID v4 generated by Keebai, not a Mongo ObjectId like the rest of the platform. Branch, service, and professional ids are ObjectIds.
Dates are YYYY-MM-DD and times are HH:mm in the branch’s timezone, not UTC and not the caller’s. If the service defines a duration you can omit end_time and Keebai computes it.
Querying availability
Three endpoints answer three different questions, and picking the right one is the difference between one request and thirty:
A booking widget almost always wants next slots for the first render and by range to populate a month view. Looping over the by-date endpoint one day at a time burns your daily quota for no benefit.
Availability is always relative to a service, because the duration and the eligible professionals come from it. There is no “what is free on Tuesday” without naming one.
Pagination
The catalog list endpoints takelimit and offset, with limit capped at 200. Availability endpoints do not paginate — they bound with maxDays and limit instead.
Branches, services, and professionals change on a human timescale. Fetch them at boot and cache; re-fetching per booking is the most common way integrations waste their quota.
Errors
Branch on
error.code, not on the message text — a 409 on create means the slot is gone, a 409 on confirm means someone already confirmed or cancelled it. Some messages are in Spanish; the codes are not.
What else fires when you book
An appointment is not an isolated record. Creating, confirming, or cancelling one also:- Delivers
scheduling.appointment.created/.rescheduled/.cancelledto your webhook subscribers, with the branch, service, professional, and customer inline. - Appends a CRM event —
scheduling.appointment_created,_confirmed, or_cancelled— to the contact’s ticket timeline, when Keebai can resolve the customer to a contact. - Queues the reminder and confirmation notifications your account has configured.
Next steps
List branches
Start from the locations available to your tenant.
Create an appointment
Book a slot with the customer embedded.
Next available slots
The one-request answer for a booking widget’s first render.
React to bookings
Mirror appointments into an external calendar without polling.