Bookings (Citas)
Citas is Recurrente’s booking product: appointments with your team, classes with limited seats, hotel stays by the night, gift cards and contracts. Everything a business does with it in the dashboard can also be done through the API, the recurrente CLI and the MCP server.
Unlike most booking APIs, the money lives on the booking: what it costs, what the payer already paid, what is left, and the checkout where they pay. You do not have to reconcile a booking with a payment yourself.
Every business account has Citas. A personal account does not: every Citas endpoint answers it with 403 and code: bookings_not_enabled.
Concepts
A service has a kind: appointment (one payer, one staff member), class (several payers in one session) or lodging (nights instead of minutes).
Book a time in three calls
1. Read the free times. GET /availability answers with the same times the public booking page shows: it subtracts what is already booked, buffers, minimum notice and the account’s daily limit.
Dates without a time (2026-10-07) are midnight in the business’s time zone. Up to 31 days per request.
2. Book one of them. Send the starts_at you were offered and identify the customer by customer_id, or by customer[phone] and customer[name] to create them. Use a unique Idempotency-Key per booking.
3. Listen for the outcome. The service’s payment policy decides what happens:
A pending booking holds the time until hold_expires_at. If nobody pays, it is cancelled by system. Subscribe to booking.confirm instead of polling.
If someone took the time first, the answer is 422 with code: slot_taken; read availability again.
The money on a booking
Change a booking
Every change answers with the booking as it is afterwards, and every one of them is idempotent with an Idempotency-Key.
Operations marked moves money need an API key with money movement enabled; otherwise they answer 403 with code: money_movement_not_enabled.
Find a booking by the customer
GET /bookings filters by status, a from/to range on the start time, staff_member_id, customer_id, class_session_id, and q, words from the customer’s name, email or phone:
Repeating bookings
POST /booking_series books the same customer, service and staff member every interval frequency (day, week, month, year, or back_to_back the same day), occurrences times or until ends_on. A time that is taken or closed is skipped and listed in skipped_starts_at instead of failing the series. Each visit is collected on the day.
Set up services and your team
A service is a product with a booking object. Send it when you create or update the product (POST /products, PATCH /products/{id}) with the same settings the dashboard’s form has: kind, duration_minutes, buffer_after_minutes, payment_policy (and the deposit or no-show fee in cents), staff_member_ids, resource_ids, add-ons and, for rooms, nightly rates. GET /services lists every bookable product with the service_id the other endpoints take.
Everything you change here is read back by GET /availability at once. Connecting a staff member’s Google Calendar is an OAuth consent and stays in the dashboard.
Classes
A class is a service with kind: class and a capacity. POST /class_sessions writes its sessions from a weekly rule (“Tuesdays and Thursdays at 5:45 pm, from October 6 to December 15”): service_id, staff_member_id, weekdays (0 is Sunday), time and the date range. It is all or nothing: if the instructor or the room is already busy at one of those times, nothing is written (busy).
A seat is a booking: POST /bookings with class_session_id. When the class is full the answer is 422 with code: full. GET /class_sessions and GET /class_sessions/{id} show the seats taken and left.
POST /class_sessions/{class_session_id}/cancellation cancels a session: every seat is cancelled as the business and refunded (a package session goes back to its package), and each customer is told. It moves money.
Waitlist
POST /waitlist_entries puts a customer on a service’s waitlist for up to five days, each whole or in part (morning, afternoon, evening), like the public page’s “Let me know”. When a booking that fits is cancelled or moved, they get a WhatsApp with the link to book it. GET /waitlist_entries lists who is waiting; DELETE /waitlist_entries/{id} takes someone off.
Lodging
A room type is a service with kind: lodging; each physical room is a resource. Stays are bookings too, with a stay object (room type, room, check-in and check-out dates, nights, guests, the open tab and the guest register).
GET /lodging/availability?check_in_on=…&check_out_on=…&guests=2answers each room type with its free rooms and the stay’s price night by night, as the public page prices it (seasons, long-stay discount, extra guests, lodging tax).POST /staysbooks it withroom_type_id, the dates and the guest. The payment policy works as for appointments.- On the day:
POST /stays/{stay_id}/check_in(opens a tab for extras),…/guest_registration(the guest register),…/date_change,…/unit_assignment, andPOST /stays/{stay_id}/check_out. Checking out closes the tab bychannel:payment_link(default),pos, orsaved_card, which charges the card and moves money.
The booking operations above (balance link, paid outside, no-show, no-show fee, cancel, refund) work on stays too. GET /bookings?kind=stay lists them, POST/DELETE /maintenance_blocks takes a room out of sale for some nights, and GET /lodging/occupancy gives the month’s occupancy and revenue.
Gift cards
The code is a bearer secret: whoever holds it spends it. Keep it out of logs. Payers redeem codes on their own checkout; that stays in the checkout.
Contracts
Signing itself always happens on the customer’s own page.
Settings, packages and notes
A line switched off takes nothing new, through the API either: with Citas off, availability by service and every new booking, series or waitlist entry answer 403 with code: bookings_off; with lodging off, a new stay answers code: lodging_off. What was already booked keeps working: moving, cancelling, collecting and the front desk.
A PATCH changes only the fields you send, with the dashboard’s validations; a refusal answers 422 with code: invalid_booking_settings and the dashboard’s own messages.
Webhooks
Every event carries the object as it was when the event happened.
Sandbox
Bookings are native in a Sandbox: the same agenda, payment links paid with the test card, and events delivered only to the Sandbox’s webhook endpoints. The no-show fee, refunds, cancelling a class and checking out run against the Sandbox’s test payments and never reach a real processor. No WhatsApp or email reaches a real person from a Sandbox: a contract sent from one is shared by its signing_url. A room’s external calendar is saved but never fetched.
Agents: CLI and MCP
Every operation above is in the agent catalog, so the recurrente CLI and the MCP server can run it without custom code. “Reschedule Ana’s 3 pm to Thursday” is three calls:
Operations that move money need a human’s confirmation: --yes in the CLI, the money permission on an MCP connection.

