Integraciones · beta
API de disponibilidad
Rutas públicas de solo lectura para mostrar en tu web o en tu sistema el catálogo y los huecos libres de un negocio. Son las mismas que usa la página de reserva de Citeri.
Beta. Estas rutas no necesitan autenticación y son estables en lo esencial, pero pueden aparecer campos nuevos. Ignora los que no conozcas. Si vamos a cambiar o quitar uno que documentamos aquí, lo avisaremos en el changelog.
| Detalle | Valor |
|---|---|
| URL base | https://citeri.app/api/book/{slug} |
| Autenticación | Ninguna |
| Formato | JSON en UTF-8 |
| Límite | 20 peticiones por minuto por IP. Ver límites. |
| CORS | No se admite desde otros dominios: llama desde tu servidor, no desde el navegador. |
| Importes | En céntimos de euro (2500 = 25,00 €). |
| Fechas y horas | AAAA-MM-DD y HH:MM en la zona horaria del negocio (org.default_tz). |
Negocio y locales#
Datos públicos del negocio, sus locales y la configuración de reserva.
curl https://citeri.app/api/book/pruebas-panelconst { org, locations } = await fetch("https://citeri.app/api/book/pruebas-panel").then((r) => r.json());
console.log(org.name, org.default_tz, locations.length);import requests
data = requests.get("https://citeri.app/api/book/pruebas-panel", timeout=10).json()
print(data["org"]["name"], data["org"]["default_tz"], len(data["locations"])){
"org": {
"id": "b2200000-0000-4000-8000-000000000002",
"name": "Salón de Pruebas",
"slug": "pruebas-panel",
"categories": ["Uñas", "Pestañas"],
"booking_enabled": true,
"booking_slot_step_min": 15,
"booking_window_days": 60,
"default_tz": "Europe/Madrid",
"…": "…"
},
"locations": [
{ "id": "b2200000-0000-4000-8000-000000000003", "name": "Salón de Pruebas",
"address": "Calle Ficticia 1", "city": "Ciudad de prueba", "has_hours": true }
],
"fields": { "…": "…" }
}| Campo | Descripción |
|---|---|
org.booking_enabled | Si el negocio acepta reservas online. |
org.booking_slot_step_min | Cada cuántos minutos empieza un hueco. |
org.booking_window_days | Con cuántos días de antelación se puede reservar. |
org.default_tz | Zona horaria de todas las fechas y horas. |
locations[].id | Para filtrar con location_id y para el parámetro loc del enlace. |
Servicios#
curl https://citeri.app/api/book/pruebas-panel/servicesconst { services } = await fetch("https://citeri.app/api/book/pruebas-panel/services").then((r) => r.json());
for (const s of services) console.log(s.name, s.duration_min, s.price_cents / 100);import requests
for s in requests.get("https://citeri.app/api/book/pruebas-panel/services", timeout=10).json()["services"]:
print(s["name"], s["duration_min"], s["price_cents"] / 100)| Campo | Descripción |
|---|---|
id | Id del servicio. Úsalo en service_id y en el parámetro service del enlace. |
name, description | Nombre y descripción. |
price_cents | Precio en céntimos. Si price_is_from es true, es un precio «desde». |
duration_min | Duración en minutos. |
categories | Categorías del servicio. |
variants | Variantes con su propio precio o duración, si las hay. |
consultation_only | true si el servicio requiere consulta previa y no se reserva directamente. |
Profesionales#
Profesionales que pueden hacer un servicio. Si el negocio oculta la elección de profesional, la lista puede venir vacía.
curl "https://citeri.app/api/book/pruebas-panel/employees?service_id=b2200000-0000-4000-8200-000000000005"{ "employees": [ { "id": "b2200000-0000-4000-8300-000000000001", "name": "Laura", "color_hex": "#5CBE7A" } ] }Horario#
weekday va de 0 (domingo) a 6 (sábado).
curl https://citeri.app/api/book/pruebas-panel/hours{ "hours": [ { "weekday": 1, "open_time": "09:30:00", "close_time": "20:00:00" } ] }Días con disponibilidad#
Días del mes en los que queda al menos un hueco para el servicio. Acepta también employee_id y location_id.
curl "https://citeri.app/api/book/pruebas-panel/dates?service_id=b2200000-0000-4000-8200-000000000005&month=2026-11"const url = new URL("https://citeri.app/api/book/pruebas-panel/dates");
url.searchParams.set("service_id", "b2200000-0000-4000-8200-000000000005");
url.searchParams.set("month", "2026-11");
const { available } = await fetch(url).then((r) => r.json());import requests
r = requests.get("https://citeri.app/api/book/pruebas-panel/dates", params={
"service_id": "b2200000-0000-4000-8200-000000000005",
"month": "2026-11",
}, timeout=10)
print(r.json()["available"]){ "available": ["2026-11-02", "2026-11-03", "2026-11-04"] }Horas libres#
Horas de inicio libres en un día. service_id y date son obligatorios; acepta también employee_id y location_id.
curl "https://citeri.app/api/book/pruebas-panel/slots?service_id=b2200000-0000-4000-8200-000000000005&date=2026-11-03"const url = new URL("https://citeri.app/api/book/pruebas-panel/slots");
url.searchParams.set("service_id", "b2200000-0000-4000-8200-000000000005");
url.searchParams.set("date", "2026-11-03");
const { slots, duration } = await fetch(url).then((r) => r.json());import requests
r = requests.get("https://citeri.app/api/book/pruebas-panel/slots", params={
"service_id": "b2200000-0000-4000-8200-000000000005",
"date": "2026-11-03",
}, timeout=10)
print(r.json()["slots"]){ "slots": ["09:30", "09:45", "10:00", "…"], "duration": 120 }Reservar#
La API no crea reservas. Cuando la clienta elija servicio y hora, llévala al enlace de reserva con &service=: ahí confirma sus datos, verifica su contacto y paga la señal si el negocio la pide.