Skip to main content
The scheduling surface backs booking flows on your own site or app: read the catalog, ask what is available, and create appointments — without sending anyone to the Keebai portal.

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.
Every endpoint on this page is also an MCP tool, so an AI agent can check availability and book with the same token and scopes.

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 returns 409 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 send company, 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.
Never build a slot timestamp by appending Z. A slot at 14:00 in a branch on America/Santiago is not 14:00Z. If you store appointments in UTC on your side, convert using the branch’s timezone, which comes back on the branch record — do not assume your server’s zone or the customer’s.A malformed date returns 400 with the offending field named: fromDate debe ser YYYY-MM-DD.

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 take limit 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 / .cancelled to 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.
If you also subscribe to those webhooks and write back into scheduling, guard against the loop.

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.