> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.recurrente.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.recurrente.com/_mcp/server.

# Citas y reservas

> Consulta horarios libres, reserva citas, clases y estadías, cobra anticipos e inasistencias y maneja la agenda desde tus sistemas o un agente

Citas es el producto de reservas de Recurrente: citas con tu equipo, clases con cupo, estadías por noche, tarjetas de regalo y contratos. Todo lo que un negocio hace con Citas en el panel también se puede hacer con la API, el CLI `recurrente` y el servidor MCP.

A diferencia de la mayoría de APIs de reservas, **el dinero está en la cita**: cuánto cuesta, cuánto pagó el cliente, cuánto falta y el checkout donde paga. No tienes que conciliar una cita con un pago por tu cuenta.

> **Note**
>
> Citas está en toda cuenta de negocio. Una cuenta personal no lo tiene: todos sus endpoints le responden `403` con `code: bookings_not_enabled`.

## Conceptos

| Objeto          | Prefijo | Qué es                                                                                                                                                       |
| --------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Servicio        | `svc_`  | Lo que hace reservable a un producto: duración, quién lo atiende, qué ocupa y qué deja el cliente para apartar la hora. Viaja en el producto como `booking`. |
| Profesional     | `stf_`  | Una persona con quien se reserva, con su horario semanal y sus ausencias.                                                                                    |
| Recurso         | `res_`  | Lo que ocupa una cita además de una persona: una silla, un cuarto, un equipo, una habitación.                                                                |
| Cita            | `bk_`   | La hora que aparta un cliente: una cita, un lugar en una clase o una estadía.                                                                                |
| Serie           | `ser_`  | Una cita que el negocio repite ("cada 2 semanas, 10 veces").                                                                                                 |
| Sesión de clase | `cls_`  | Una fecha de una clase, con su cupo.                                                                                                                         |

Un servicio tiene un `kind`: `appointment` (un cliente, un profesional), `class` (varios clientes en una sesión) o `lodging` (noches en vez de minutos).

## Reservar en tres llamadas

**1. Consulta los horarios libres.** `GET /availability` responde los mismos horarios que muestra la página pública de citas: resta lo ya reservado, el tiempo extra después de cada cita, la anticipación mínima y el límite diario de la cuenta.

```bash
curl "https://app.recurrente.com/api/availability?service_id=svc_ab12cd34&from=2026-10-07&to=2026-10-08" \
  -H "X-SECRET-KEY: sk_live_..."
```

```json
[
  { "starts_at": "2026-10-07T09:00:00-06:00", "ends_at": "2026-10-07T10:00:00-06:00",
    "staff_member_id": "stf_ab12cd34", "resource_id": null, "class_session_id": null, "seats_left": null }
]
```

Una fecha sin hora (`2026-10-07`) es la medianoche en la zona horaria del negocio. Hasta 31 días por consulta.

**2. Reserva uno.** Envía el `starts_at` que te ofrecieron e identifica al cliente con `customer_id`, o con `customer[phone]` y `customer[name]` para crearlo. Usa un `Idempotency-Key` distinto por cita.

```bash
curl -X POST https://app.recurrente.com/api/bookings \
  -H "X-SECRET-KEY: sk_live_..." -H "Idempotency-Key: cita-7781" \
  -H "Content-Type: application/json" \
  -d '{"service_id":"svc_ab12cd34","starts_at":"2026-10-07T09:00:00-06:00",
       "customer":{"name":"Lucía Pérez","phone":"+50255551234"}}'
```

**3. Escucha el resultado.** La política de pago del servicio decide qué pasa:

| Política       | Estado al reservar                                              | Qué hace el cliente                                                              |
| -------------- | --------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `none`         | `confirmed` (o `pending` si el negocio acepta cada cita a mano) | Nada.                                                                            |
| `card_on_file` | `pending` con `checkout_url`                                    | Deja una tarjeta para el cargo por inasistencia; la cita se confirma al dejarla. |
| `deposit`      | `pending` con `checkout_url`                                    | Paga el anticipo; la cita se confirma al pagarlo.                                |
| `full`         | `pending` con `checkout_url`                                    | Paga el total.                                                                   |

Una cita `pending` aparta la hora hasta `hold_expires_at`. Si nadie paga, la cancela `system`. Suscríbete a `booking.confirm` en vez de consultar una y otra vez.

Si alguien tomó la hora antes, la respuesta es `422` con `code: slot_taken`; vuelve a consultar los horarios.

## El dinero de una cita

| Campo                     | Qué significa                                                                                               |
| ------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `total_in_cents`          | Lo que cuesta la cita, congelado al reservar.                                                               |
| `paid_in_cents`           | Lo que el cliente ya pagó (anticipo, prepago, protección, saldo).                                           |
| `balance_in_cents`        | Lo que falta cobrar el día de la cita. `0` en una sesión de paquete o un saldo cobrado fuera de Recurrente. |
| `checkout_url`            | Donde el cliente paga lo que pide la política, o deja su tarjeta.                                           |
| `balance_checkout_url`    | El link de pago del saldo, una vez lo pides.                                                                |
| `no_show_fee_checkout_id` | El checkout del cargo por inasistencia, cobrado o rechazado.                                                |

## Cambiar una cita

Cada cambio responde la cita como queda, y todos son idempotentes con un `Idempotency-Key`.

| Operación                                     | Endpoint                                                 | Notas                                                                                                                                                               |
| --------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Moverla                                       | `POST /bookings/{booking_id}/reschedule`                 | Con el mismo profesional. La respuesta es la cita nueva; `rescheduled_from_id` apunta a la anterior, que queda cancelada por `system`. Lo pagado se mueve con ella. |
| ¿A qué hora se puede mover?                   | `GET /availability?booking_id=…`                         | Su propia hora cuenta como libre.                                                                                                                                   |
| Cancelarla                                    | `POST /bookings/{booking_id}/cancellation`               | El negocio siempre puede cancelar. No devuelve nada.                                                                                                                |
| Cancelar y devolver, o devolver una cancelada | `POST /bookings/{booking_id}/refund`                     | Devuelve lo que cobró la cita por los mismos pagos por los que entró. Mueve dinero.                                                                                 |
| Aceptar o rechazar                            | `POST /bookings/{booking_id}/acceptance`, `/declination` | Para negocios que revisan cada cita. Rechazar devuelve el pago de una cita prepagada.                                                                               |
| Completarla                                   | `POST /bookings/{booking_id}/completion`                 | Cuando ya llegó su hora.                                                                                                                                            |
| Marcar inasistencia                           | `POST /bookings/{booking_id}/no_show`                    | No cobra nada por sí misma.                                                                                                                                         |
| Cobrar la inasistencia                        | `POST /bookings/{booking_id}/no_show_fee`                | A la tarjeta que dejó el cliente. Nunca es automático. Un rechazo responde `code: declined`. Mueve dinero.                                                          |
| Cobrar el saldo                               | `POST /bookings/{booking_id}/balance_charge`             | Crea (o reutiliza) el link de pago del saldo. No le cobra a nadie.                                                                                                  |
| Saldo pagado afuera                           | `POST /bookings/{booking_id}/outside_payment`            | El negocio lo cobró en efectivo o en su propia terminal. Mueve dinero.                                                                                              |
| Nota sobre el cliente                         | `POST /bookings/{booking_id}/customer_notes`             | Aparece también en sus próximas citas.                                                                                                                              |

Las operaciones que *mueven dinero* necesitan una llave con movimiento de dinero habilitado; si no, responden `403` con `code: money_movement_not_enabled`.

### Encontrar una cita por el cliente

`GET /bookings` filtra por `status`, un rango `from`/`to` sobre la hora de inicio, `staff_member_id`, `customer_id`, `class_session_id` y `q`, palabras del nombre, correo o teléfono del cliente:

```bash
curl "https://app.recurrente.com/api/bookings?q=Ana&from=2026-10-06T00:00:00-06:00&to=2026-10-07T00:00:00-06:00" \
  -H "X-SECRET-KEY: sk_live_..."
```

### Citas que se repiten

`POST /booking_series` reserva al mismo cliente, servicio y profesional cada `interval` `frequency` (`day`, `week`, `month`, `year`, o `back_to_back` el mismo día), `occurrences` veces o hasta `ends_on`. Una hora ocupada o cerrada se salta y aparece en `skipped_starts_at` en vez de detener la serie. Cada visita se cobra el día de la cita.

## Configura tus servicios y tu equipo

Un servicio es un producto con un objeto `booking`. Envíalo al crear o actualizar el producto (`POST /products`, `PATCH /products/{id}`) con los mismos ajustes del formulario del panel: `kind`, `duration_minutes`, `buffer_after_minutes`, `payment_policy` (y el anticipo o el cargo por inasistencia en centavos), `staff_member_ids`, `resource_ids`, extras y, para habitaciones, tarifas por noche. `GET /services` lista todos los productos que se reservan con el `service_id` que piden los demás endpoints.

| Qué                                         | Endpoints                                                                                                                    |
| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| Profesionales                               | `GET/POST /staff_members`, `GET/PATCH/DELETE /staff_members/{id}` (`position` los ordena)                                    |
| La semana y las ausencias de un profesional | `GET/PUT /staff_members/{staff_member_id}/schedule`, `GET/POST/DELETE …/availability_exceptions`                             |
| Recursos (sillas, cuartos, equipos)         | `GET/POST /resources`, `GET/PATCH/DELETE /resources/{id}`, y los calendarios externos de una habitación en `…/ical_feeds`    |
| Horario del negocio y fechas especiales     | `GET/PUT /business_hours`, `GET/POST/DELETE /business_hours/availability_exceptions` (`closed`, u `hours` para un día corto) |

Todo lo que cambias aquí lo lee `GET /availability` de inmediato. Conectar el Google Calendar de un profesional es un permiso de OAuth y se queda en el panel.

## Clases

Una clase es un servicio con `kind: class` y un `capacity`. `POST /class_sessions` escribe sus sesiones con una regla semanal ("martes y jueves a las 5:45 pm, del 6 de octubre al 15 de diciembre"): `service_id`, `staff_member_id`, `weekdays` (0 es domingo), `time` y el rango de fechas. Es todo o nada: si el instructor o el salón ya están ocupados en alguna de esas horas, no escribe ninguna (`busy`).

Un lugar es una cita: `POST /bookings` con `class_session_id`. Si la clase está llena, la respuesta es `422` con `code: full`. `GET /class_sessions` y `GET /class_sessions/{id}` muestran los lugares tomados y libres.

`POST /class_sessions/{class_session_id}/cancellation` cancela una sesión: cada lugar se cancela como el negocio y se devuelve (una sesión de paquete vuelve a su paquete), y cada cliente recibe el aviso. Mueve dinero.

## Lista de espera

`POST /waitlist_entries` pone a un cliente en la lista de espera de un servicio para hasta cinco días, cada uno entero o una parte (`morning`, `afternoon`, `evening`), como el "Avísame" de la página pública. Cuando se cancela o se mueve una cita que le sirve, recibe un WhatsApp con el link para reservarla. `GET /waitlist_entries` lista quién espera; `DELETE /waitlist_entries/{id}` quita a alguien.

## Alojamiento

Un tipo de habitación es un servicio con `kind: lodging`; cada habitación física es un recurso. Las estadías también son citas, con un objeto `stay` (tipo, habitación, fechas de llegada y salida, noches, huéspedes, la cuenta abierta y el registro de huéspedes).

1. `GET /lodging/availability?check_in_on=…&check_out_on=…&guests=2` responde cada tipo de habitación con sus habitaciones libres y el precio de la estadía noche por noche, como lo calcula la página pública (temporadas, descuento por estadía larga, huéspedes extra, impuesto de hospedaje).
2. `POST /stays` la reserva con `room_type_id`, las fechas y el huésped. La política de pago funciona igual que en las citas.
3. El día de la llegada: `POST /stays/{stay_id}/check_in` (abre una cuenta para los extras), `…/guest_registration` (el registro de huéspedes), `…/date_change`, `…/unit_assignment`, y `POST /stays/{stay_id}/check_out`. El check-out cierra la cuenta según `channel`: `payment_link` (por defecto), `pos`, o `saved_card`, que cobra la tarjeta y mueve dinero.

Las operaciones de citas de arriba (link del saldo, pagado afuera, inasistencia, cargo por inasistencia, cancelar, devolver) también funcionan con estadías. `GET /bookings?kind=stay` las lista, `POST/DELETE /maintenance_blocks` saca una habitación de venta unas noches y `GET /lodging/occupancy` da la ocupación y los ingresos del mes.

## Tarjetas de regalo

| Operación         | Endpoint                                     | Notas                                                                                                                                |
| ----------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| Vender o recargar | `POST /gift_cards`                           | Abre el checkout que vende una tarjeta (o recarga `gift_card_id`). No le cobra a nadie por sí mismo: la tarjeta se emite al pagarse. |
| Listar, leer      | `GET /gift_cards`, `GET /gift_cards/{id}`    | Con el saldo y cada movimiento. `q` busca por código, o por nombre o correo de quien la recibe.                                      |
| Canjear           | `POST /gift_cards/{gift_card_id}/redemption` | Gasta saldo por una venta cobrada fuera de un checkout de Recurrente. Mueve dinero.                                                  |
| Ajustar           | `POST /gift_cards/{gift_card_id}/adjustment` | `credit` o `debit`, con una nota. Mueve dinero.                                                                                      |
| Anular            | `POST /gift_cards/{gift_card_id}/void`       | La tarjeta deja de funcionar y pierde su saldo. Mueve dinero.                                                                        |
| Configuración     | `GET/PATCH /gift_card_settings`              | Si el negocio vende tarjetas de regalo y cuántos meses dura una tarjeta.                                                             |

El código funciona como efectivo: quien lo tiene lo gasta. Mantenlo fuera de tus logs. Los clientes canjean códigos en su propio checkout; eso se queda en el checkout.

## Contratos

| Operación                        | Endpoint                                                                          | Notas                                                                                                                                                                     |
| -------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Plantillas                       | `GET/POST /contract_templates`, `GET/PATCH/DELETE /contract_templates/{id}`       | `body` es HTML con variables (`{nombre}`, `{servicio}`, `{fecha}`…); `fields` son los datos que llena el cliente; `service_ids` la envía con cada cita de esos servicios. |
| Borrador con IA                  | `POST /contract_template_drafts`                                                  | Describe qué debe aceptar el cliente; la respuesta es una plantilla sin guardar para revisar. 20 por hora.                                                                |
| Enviar                           | `POST /contracts`                                                                 | Por WhatsApp y correo, con recordatorio a las 48 horas. `charge_amount_in_cents` pide firmar antes de pagar.                                                              |
| Enviar los contratos de una cita | `POST /bookings/{booking_id}/contract_delivery`                                   | Los que piden sus servicios y aún no se enviaron.                                                                                                                         |
| Seguimiento                      | `GET /contracts`, `GET /contracts/{id}`, `POST /contracts/{contract_id}/reminder` | `signing_url` es la página donde firma el cliente.                                                                                                                        |
| PDF firmado                      | `GET /contracts/{contract_id}/pdf`                                                | `409 pdf_not_ready` hasta que esté firmado.                                                                                                                               |

La firma siempre ocurre en la página del cliente.

## Configuración, paquetes y notas

| Qué                                                                                      | Endpoints                                                                                                                                                  |
| ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Activar o desactivar una forma de vender (store, citas, lodging, gift\_cards, contracts) | `GET /sales_channels`, `PUT /sales_channels/{key}` con `{"on": false}`                                                                                     |
| Políticas (anticipación, ventana, cancelaciones, anticipos, inasistencia, aceptación)    | `GET/PATCH /booking_settings/policy`                                                                                                                       |
| Qué mensajes salen                                                                       | `GET/PATCH /booking_settings/notifications`                                                                                                                |
| Alojamiento (habitaciones por noche, impuesto de hospedaje)                              | `GET/PATCH /booking_settings/lodging`                                                                                                                      |
| Los links públicos para compartir                                                        | `GET /booking_settings/share`                                                                                                                              |
| Paquetes de sesiones vendidos por adelantado                                             | `GET/POST /booking_packages`, `DELETE /booking_packages/{id}`, y las sesiones que le quedan a un cliente en `GET /customers/{customer_id}/package_credits` |
| Notas sobre un cliente                                                                   | `GET/POST /customers/{customer_id}/notes`, `DELETE …/notes/{id}`                                                                                           |

Una forma de vender desactivada no recibe nada nuevo, tampoco por la API: con Citas desactivado, los horarios por servicio y toda cita, serie o lugar en la lista de espera nuevos responden `403` con `code: bookings_off`; con Alojamiento desactivado, una estadía nueva responde `code: lodging_off`. Lo ya reservado sigue funcionando: moverlo, cancelarlo, cobrarlo y la recepción.

Un `PATCH` cambia solo los campos que envías, con las validaciones del panel; si no se puede, responde `422` con `code: invalid_booking_settings` y los mismos mensajes del panel.

## Webhooks

Cada evento lleva el objeto como estaba en el momento del evento.

| Evento                                                                                                             | Cuándo                                                                               |
| ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
| `booking.create`                                                                                                   | Se reservó una cita o un lugar en una clase, desde cualquier canal.                  |
| `booking.confirm`                                                                                                  | La hora es del cliente: reservada sin pago, pagada o aceptada.                       |
| `booking.reschedule`                                                                                               | Se emite sobre la cita nueva cuando una cita se mueve.                               |
| `booking.cancel`                                                                                                   | Cancelada; `cancelled_by` dice quién.                                                |
| `booking.refund`                                                                                                   | Se devolvió lo que la cita había cobrado.                                            |
| `booking.no_show`                                                                                                  | Marcada como inasistencia.                                                           |
| `booking.complete`                                                                                                 | La visita ocurrió.                                                                   |
| `booking.check_in` / `booking.check_out`                                                                           | El huésped de una estadía llegó / salió.                                             |
| `gift_card.issue`, `.reload`, `.redeem`, `.release`, `.adjust`, `.refund_back`, `.refund_void`, `.expire`, `.void` | Uno por cada movimiento del saldo de una tarjeta, con la tarjeta y su `transaction`. |
| `contract.send`, `.view`, `.sign`, `.decline`, `.expire`                                                           | Un contrato cambió de estado.                                                        |

## Sandbox

Las citas son nativas en un Sandbox: la misma agenda, links de pago que se pagan con la tarjeta de prueba y eventos que salen solo a los webhooks del Sandbox. El cargo por inasistencia, las devoluciones, cancelar una clase y el check-out corren sobre los pagos de prueba del Sandbox y nunca llegan a un procesador real. Ningún WhatsApp ni correo le llega a una persona real desde un Sandbox: un contrato enviado desde uno se comparte con su `signing_url`. El calendario externo de una habitación se guarda, pero nunca se descarga.

## Agentes: CLI y MCP

Todas las operaciones de esta guía están en el catálogo de agentes, así que el CLI `recurrente` y el servidor MCP las corren sin código adicional. "Mueve la cita de Ana de las 3 al jueves" son tres llamadas:

```bash
recurrente bookings list --q Ana
recurrente bookings availability --booking-id bk_ab12cd34 --from 2026-10-08 --to 2026-10-09
recurrente bookings reschedule --booking-id bk_ab12cd34 --starts-at 2026-10-08T15:00:00-06:00
```

Las operaciones que mueven dinero necesitan que una persona confirme: `--yes` en el CLI, el permiso `money` en una conexión MCP.