API de resbok (1.17.0)

Download OpenAPI specification:

Catálogo, disponibilidad, reservaciones y avisos de resbok para sistemas que se conectan (callcenter, bots, integraciones).

API para que otro sistema (un callcenter con bot telefónico y agentes, un asistente, una integración propia) conozca los restaurantes de resbok, sus reglas de reservación y la disponibilidad real, reserve, dé seguimiento y reciba avisos cuando una reservación cambia.

Dirección

  • https://api.resbok.com/v1 — la dirección de la API desde el 1-oct-2026. Esta página se abre ahí mismo, sin token.
  • https://mesas.masmesa.com/callcenter/v1 — la dirección anterior. Sigue funcionando igual (mismas rutas, mismos tokens, mismas respuestas): quien ya está conectado no tiene que cambiar nada. Las integraciones nuevas usan la primera.

Historia

  • Etapa A: catálogo y disponibilidad.

  • Etapa B: crear reservaciones con folio y consultarlas.

  • Etapa C: modificar y cancelar (servicio a clientes).

  • Etapa D: nivel alto: leer todas las reservaciones de resbok, de cualquier canal (solo lectura).

  • Etapa E (pedido de CallMarket): turnos de reservación por día en la ficha; direccion.colonia con la colonia real y direccion.alcaldia nueva; nombres_alternos (también en la búsqueda q); GET /salud.

  • Etapa E2 (pedido de CallMarket): cuando está cerrado, mensaje con el horario de reservaciones de ese día (o el siguiente día que abre), horario_del_dia y alternativas del siguiente día con reservaciones; promociones con vigente_desde y vigente_hasta.

  • Etapa H1 ("Lucy hostess"): la ficha trae contacto (Instagram, Facebook, micrositio, Google Maps), fotos (principal, logo, galería), servicios (con texto para leer), calificacion, areas y aforo estimado.

  • Etapa H2 ("Lucy hostess"): area_id opcional para consultar, reservar y modificar en un área (terraza, salón…); areas_con_lugar y ocupacion ("últimos lugares") en la disponibilidad; area en la reservación.

  • Versión 1.8: la ficha trae contacto.pagina_masmesa y fotos.menu; contacto.micrositio solo con dominios que el lugar usa.

  • Versión 1.9: jornada — el horario y la reservación dicen a qué noche pertenecen, porque los turnos cruzan la medianoche. Además, un restaurante con los turnos mal cargados ya no se ofrece como reservable.

  • Versión 1.10: se puede preguntar por la noche (jornada) en vez de por el día del calendario, y el texto de la jornada habla siempre de esa noche.

  • Versión 1.11: teléfonos de cualquier país, con pais o con su lada.

  • Versión 1.12: segundo teléfono opcional, fecha de nacimiento validada y festejado.

  • Versión 1.13: ocasión del catálogo, festejado guardado aparte, promociones y eventos reales de "Mi sitio".

  • Versión 1.14: experiencias de "Mi sitio" (paquetes que se reservan como una mesa y se pagan en el lugar).

  • Versión 1.15: el aviso (webhook) sale por todas las reservaciones de los restaurantes habilitados, de cualquier canal, con más datos (evento, canal, cliente, jornada).

  • Versión 1.16: respuesta del cliente al seguimiento de asistencia (POST /reservaciones/{folio}/respuesta-cliente), marcado_por: "cliente" en los avisos y el motivo sin_dueno con lugares recomendados.

  • Versión 1.17 (esta): la madrugada pertenece a la noche anterior también en la agenda, qr_url con el QR del folio, webhooks por integración y la dirección nueva https://api.resbok.com/v1.

Cambios de la versión 1.17 (no rompen)

  • Dirección nueva: https://api.resbok.com/v1. La anterior sigue funcionando sin cambios.
  • qr_url en las respuestas de reservación (POST /reservaciones, GET /reservaciones/{folio}, las listas y la agenda): liga pública a un PNG con el QR del folio. Se le puede mandar al cliente por WhatsApp; lo muestra en la puerta y la hostess lo escanea. Con ?descargar=1 el navegador lo guarda como archivo.
  • Agenda por noche: GET /restaurantes/{id}/reservaciones?fecha=2026-09-25 devuelve toda la noche del 25, incluida su madrugada del 26, y ya no la madrugada del 25 (que es de la noche del 24). fecha y hora de cada reservación siguen siendo las del reloj; jornada dice a qué noche pertenece.
  • Webhooks por integración: cada integración puede tener su propia URL de avisos, sus eventos y su secreto (ver Avisos (webhooks) más abajo).

Cambios de la versión 1.16 (no rompen)

  • POST /reservaciones/{folio}/respuesta-cliente: lo que contestó el cliente ("sí fui" / "no fui") al día siguiente. Basta un "sí" del cliente o de la hostess para que cuente como asistencia.
  • Si la hostess no marca "no llegó" en 48 h, la reservación cuenta como asistida (marcado_por: "sistema").
  • Motivo sin_dueno en GET /disponibilidad: nadie ha reclamado la ficha de ese lugar; no se toma reservación ni solicitud y la respuesta trae hasta 3 recomendados de la misma zona que sí reservan.

Cambios de la versión 1.15 (no rompen)

  • El aviso sale por cualquier reservación de un restaurante habilitado (portal, micrositio, libro, Google, otra integración), no solo por las que creó quien recibe el aviso.
  • Campos nuevos en el aviso: nombre, apellido, telefono, correo, restaurante_id, jornada, origen, evento (nueva | cambio), canal, pais, telefono_internacional y, al marcar asistencia, marcado_por.

Cambios de la versión 1.14 (no rompen)

  • Experiencias en la ficha: experiencias[] con título, resumen, precio_texto listo para leer ("$4,500 el paquete (hasta 8 personas)"), personas_texto, días (cada_semana), hora_desde/hora_hasta, proximas_fechas (14 días) y cupo_por_dia. El precio es solo informativo: se paga en el lugar, no se cobra en línea.
  • Disponibilidad: experiencias que aplican esa noche (la 01:00 del sábado cuenta como el viernes).
  • Reservar con experiencia: experiencia_id opcional en POST /reservaciones. Se revisa que aplique esa noche, a esa hora, para esas personas y que quede cupo; si no, 422 experiencia_no_aplica con mensaje para leer al cliente y motivo (dia, hora, personas, sin_cupo, no_disponible), y no se crea nada. Si aplica, la respuesta trae experiencia (id, titulo, total_estimado, se_paga_en_el_lugar) y la nota del restaurante empieza con "Experiencia: …". modo_prueba revisa lo mismo sin ocupar cupo.

Cambios de la versión 1.13 (no rompen, salvo el origen de las promociones)

  • Ocasión del catálogo: ocasion_id al crear y al modificar (GET /ocasiones: 1 Reserva Casual, 2 Cumpleaños, 3 Aniversario, 4 Negocios, 5 Cita, 6 Salida con amigos, 7 Despedida, 8 Otra). El texto ocasion de siempre sigue funcionando y además se traduce al catálogo. La reservación trae ocasion ({id, nombre}) y festejado, guardados aparte de la nota; la nota sigue igual. Una ocasión que no existe → 422 en campos.ocasion_id.
  • Promociones de cumpleaños al reservar: con la ocasión Cumpleaños, POST /reservaciones responde promociones_aplicables con las de cumpleaños del lugar que aplican ese día, para ofrecerlas. Solo informativo.
  • Promociones reales: promociones[] de la ficha ahora son las que el lugar publica en "Mi sitio" (las mismas de su micrositio), con los campos nuevos tipo (dias | cumpleanos), dias y condiciones, y vigencia.tipo puede ser cumpleanos. Antes salían de las "experiencias" de resbok, que solo tenían datos de prueba: un lugar que no publica promociones en Mi sitio ahora trae la lista vacía. Los campos de precio y descuento siguen, en null, y prepago en false.
  • Eventos: la ficha trae eventos (próximos 14 días, máximo 10) y GET /disponibilidad trae los eventos de esa noche, para decir "este viernes hay DJ".

Cambios de la versión 1.12 (no rompen)

  • Segundo teléfono opcional: telefono_2 + pais_2 al crear (POST /reservaciones) y al modificar (PATCH /reservaciones/{folio}), con la misma regla que telefono + pais. Al modificar se puede mandar solo, y vacío o null lo borra. Inválido → 422 en campos.telefono_2 ("El segundo teléfono no es válido para el país indicado."); país que no existe → campos.pais_2. Con horario nuevo sin lugar no se cambia nada.
  • Respuestas (reservación y agenda): cliente.telefono_2, cliente.pais_2 y cliente.telefono_2_internacional, null si no hay.
  • fecha_nacimiento (ya existía) se guarda en la ficha del cliente del restaurante; ahora además no puede ser futura ni anterior a 1900 → 422 en campos.fecha_nacimiento.
  • festejado opcional (máx. 128): de quién es el festejo si no es de quien reserva. Va a la nota que ve el restaurante como "Cumpleaños de: Luis" (o "Festejado: Luis" si la ocasion no es un cumpleaños). nombre_festejado sigue funcionando igual. Antes la nota decía "Festejado: Luis" también en cumpleaños. No se devuelve como campo: queda en nota, que el restaurante puede editar.
  • Lo que ya mandan no cambia.

Cambios de la versión 1.11 (no rompen)

  • Lo de siempre no cambia: telefono de 10 dígitos sin pais es de México, con los mismos mensajes de error.
  • pais opcional (ISO de 2 letras: MX, US, ES…) al crear, consultar, modificar y cancelar, y en GET /reservaciones, GET /reservaciones/{folio} y GET /agenda/reservaciones. También se acepta el teléfono con su lada ("+1 212 555 1234", "+34 612 345 678"); la lada escrita manda sobre pais.
  • Cada país con su largo válido (México 10 dígitos; EE. UU. y Canadá 10; España 9…). Número que no sirve para su país → 422 con "El teléfono no es válido para el país indicado."; país que no existe → 422 en campos.pais.
  • Folio + teléfono identifica al cliente con número y país: los mismos 10 dígitos como número de México y como número de EE. UU. son clientes distintos. Si crearon la reservación con pais, consúltenla con el mismo pais (o con la lada).
  • Respuestas: cliente.pais y cliente.telefono_internacional ("+525512345678", listo para marcar), junto a cliente.telefono, que sigue siendo el número nacional. En la agenda son null si el número guardado por otro canal no se reconoce.

Cambios de la versión 1.10 (no rompen)

  • GET /disponibilidad acepta jornada=AAAA-MM-DD en lugar de fecha: es la noche a la que pertenece la hora, como la dice el cliente. jornada=2026-09-18&hora=02:00 ("el viernes a las 2 de la mañana") consulta el sábado 19 a las 02:00 y responde con la fecha real. Las dos juntas, o ninguna, responden 422 parametros_invalidos.

  • El bloque jornada de la respuesta ahora habla de la noche a la que pertenece la hora consultada, con el horario de esa noche. Antes tomaba el día del calendario: preguntando por el sábado a las 02:00 decía "El sábado…", que confundía a quien había preguntado por el viernes. Si ya leen ese texto, ahora dice lo que esperaban.

  • Aclaración (no cambia el comportamiento): la hora de cierre del turno se puede reservar. En un turno de 23:00 a 01:00, la 01:00 es válida; la primera que ya no entra es la 01:15.

  • Motivo cerrado_por_el_lugar en GET /disponibilidad: el restaurante cerró sus reservaciones de ese día (evento, reservas solo en el local o cerrado). No trae alternativas y no debe ofrecerse como "lleno".

Cambios de la versión 1.9 (no rompen)

  • disponibilidad: campo nuevo jornada (fecha, dia, ventanas, texto), con el horario dicho como lo entiende el cliente y listo para leerse por teléfono: "El viernes 18 de septiembre recibe reservaciones de 22:00 a 01:00. La 01:00 es de la madrugada del sábado y cuenta como la noche del viernes." Es del día que se consultó, y null cuando no hay horario que decir (ese día no abre, o el motivo no es cerrado).
  • Cada ventana de horario_del_dia trae termina_al_dia_siguiente.
  • Reservación: campo nuevo jornada (fecha, dia, es_madrugada, texto) con la noche a la que pertenece la mesa. fecha y hora siguen siendo las del calendario real; jornada.fecha es el día del turno, el mismo con que la reservación queda en el libro del restaurante. La madrugada (hasta las 05:45) pertenece a la noche anterior.
  • Restaurantes con los turnos mal cargados: un lugar solo aparece como reservable en línea si su turno es coherente por dentro y tiene al menos una mesa activa. Los que no lo cumplen responden no_acepta_api y ya no devuelven horario, en vez de anunciar un rango que después rechazaban a cualquier hora.

Cambios de la versión 1.8 (no rompen)

  • Ficha: contacto.pagina_masmesa (página del lugar en www.masmesa.com) y fotos.menu (fotos o ligas de la carta, las mismas del micrositio).
  • contacto.micrositio ya no trae dominios que el restaurante dejó de usar (cuando su sitio web capturado apunta a otra parte).

Cambios de la versión 1.7 (no rompen)

  • GET /disponibilidad: parámetro opcional area_id; campos nuevos area_id, areas_con_lugar y ocupacion.
  • POST /reservaciones y PATCH /reservaciones/{folio}: campo opcional area_id. Un área que no es del restaurante → 422 area_invalida.
  • Reservación: campo nuevo area (área de la mesa asignada).

Cambios de la versión 1.6 (no rompen)

  • Ficha: campos nuevos contacto, fotos, servicios, calificacion, areas, aforo.

Cambios de la versión 1.5 (no rompen)

  • disponibilidad: campo nuevo horario_del_dia (vacío salvo motivo = cerrado). El mensaje de cerrado ahora agrega el horario del día; si su código compara el texto exacto, cámbienlo por motivo.
  • promociones[]: campos nuevos vigente_desde y vigente_hasta.

Cambios de la versión 1.4 que afectan a quien ya integró

  • direccion.colonia ahora es la colonia como la dice la gente (Polanco, Condesa). Antes traía la alcaldía, que ahora viene en direccion.alcaldia. Puede venir null si resbok todavía no la captura para un lugar nuevo.
  • El filtro colonia busca en la colonia y en la alcaldía.
  • politicas.tolerancia ya no viene null cuando la tolerancia es "Ilimitado": trae el texto para leerlo.
  • Campos nuevos (no rompen): nombres_alternos, turnos, direccion.alcaldia y en reglas: tolerancia_ilimitada, mesas_maximo, personas_de_pie_maximo, intervalo_minutos (todo el formulario de configuración del dashboard).

Acceso

  • Token fijo por cliente en la cabecera Authorization: Bearer <token> (también se acepta X-API-Key: <token>).
  • Solo HTTPS.
  • Límite de peticiones por minuto por token; al pasarlo responde 429. Muchos tokens inválidos desde la misma IP también dan 429 (un token válido siempre pasa).
  • Cada token tiene permisos (ver catálogo, ver disponibilidad, crear, consultar, modificar, cancelar, ver todas las reservas). Sin el permiso de una operación responde 403. resbok entrega un token con todos los permisos; el callcenter decide qué extensión o área usa cada operación (por ejemplo, reservar en una y dar seguimiento en otra). Para modificar o cancelar siempre se confirma con el folio y el teléfono de la reservación.

Formato

  • JSON en UTF-8.
  • Fechas AAAA-MM-DD y horas HH:MM en 24 h, en la zona horaria del restaurante (zona_horaria).
  • Las horas de reservación van en cuartos de hora (:00, :15, :30, :45).

Errores

Todos los errores responden {"error": "<código>", "mensaje": "<texto>"}. El mensaje está escrito para leerse tal cual al cliente.

Reglas que aplica la API

  • Catálogo siempre al día: aparecen todos los restaurantes activos en resbok, los mismos del portal. Uno nuevo aparece solo; uno dado de baja deja de aparecer y su ficha responde 404. Sincronicen GET /restaurantes periódicamente (por ejemplo cada hora) y usen actualizado_en para saber qué cambió.
  • Buscar por teléfono (GET /reservaciones?telefono=, GET /agenda/reservaciones?telefono=) lleva el teléfono en la URL: no la guarden en bitácoras de su lado.
  • acepta_reservas_por_api = true: se reserva en línea igual que en el portal. false: el lugar no reserva en línea. Se registra una solicitud pendiente, pero antes se revisan personas y anticipación: un grupo fuera de rango o una hora pasada se rechaza igual.
  • Mínimo y máximo de personas, anticipación mínima y porcentaje de asistencia salen de la configuración de cada restaurante en resbok; si el restaurante la cambia, la API lo refleja de inmediato.

Probar conexión y token

Respuesta mínima para comprobar que el API responde y que el token es válido, sin bajar el catálogo. Sirve con cualquier token activo (no pide permiso especial) y no se registra en la bitácora de resbok. Cuenta para el límite de peticiones por minuto: no lo consulten más de una vez por minuto.

Authorizations:
bearerAuthapiKey

Responses

Response samples

Content type
application/json
{
  • "estado": "ok",
  • "hora_servidor": "2026-09-14 16:35:50"
}

Lista de restaurantes con su ficha completa

Devuelve todos los restaurantes activos de resbok (los mismos del portal), ordenados por nombre, con la ficha completa de cada uno. Pensado para sincronizar el catálogo del bot cada cierto tiempo. Todos los filtros son opcionales, buscan texto parcial y no distinguen mayúsculas ni acentos.

Authorizations:
bearerAuthapiKey
query Parameters
q
string
Example: q=compa

Nombre del lugar o uno de sus nombres_alternos ("el chateau", "santana condesa").

ciudad
string
Example: ciudad=Metepec

Ciudad o alcaldía.

estado
string
Example: estado=Ciudad de México

Estado.

colonia
string
Example: colonia=Polanco

Colonia (Polanco, Condesa) o alcaldía (Miguel Hidalgo, Cuauhtémoc).

tipo
string
Example: tipo=Vida nocturna

Tipo de lugar (Restaurante, Vida nocturna, Club de playa, Eventos).

cocina
string
Example: cocina=Mexicana

Tipo de cocina (Mexicana, Bar, Discoteca…).

Responses

Response samples

Content type
application/json
{
  • "restaurantes": [
    ],
  • "total": 1
}

Ficha técnica de un restaurante

Misma ficha que en la lista, para un solo restaurante.

Authorizations:
bearerAuthapiKey
path Parameters
id
required
integer >= 1
Example: 3

Id del restaurante en resbok.

Responses

Response samples

Content type
application/json
Example
{
  • "activo": true,
  • "actualizado_en": "2026-09-14 16:23:27",
  • "aforo": {
    },
  • "areas": [
    ],
  • "calificacion": {
    },
  • "cocina": [
    ],
  • "descripcion": "Sumérgete en la auténtica experiencia brasileña en nuestro restaurante churrascaria. Ofrecemos cortes de carne de primera calidad y una amplia variedad de opciones de barbacoa. ¡Ven y disfruta de nuestro ambiente acogedor y de nuestro servicio excepcional!",
  • "descripcion_corta": "Sumérgete en la auténtica experiencia brasileña en nuestro restaurante churrascaria.",
  • "direccion": {
    },
  • "horarios": [ ],
  • "id": 3,
  • "nombre": "Churrascaría",
  • "nombres_alternos": [
    ],
  • "politicas": {
    },
  • "promociones": [
    ],
  • "eventos": [
    ],
  • "experiencias": [ ],
  • "rango_precio": {
    },
  • "reglas": {
    },
  • "servicios": {
    },
  • "sitio_web": "https://churrascaria.mx/",
  • "telefono": "55 5678 2854",
  • "tipo": [
    ],
  • "turnos": [
    ],
  • "zona_horaria": "America/Mexico_City"
}

Catálogo de ocasiones

(1.13) Ocasiones de resbok (las mismas del portal, los micrositios y el libro del restaurante) para mandar ocasion_id al reservar o al cambiar. Los ids no cambian; resbok puede ajustar los nombres. Requiere el permiso catalogo.leer.

Authorizations:
bearerAuthapiKey

Responses

Response samples

Content type
application/json
{
  • "total": 8,
  • "ocasiones": [
    ]
}

Disponibilidad

Si hay lugar en un día, hora y número de personas, con alternativas.

¿Hay lugar?

Consulta el motor real de resbok. Si no hay lugar, explica el motivo y ofrece hasta 3 alternativas, primero las del mismo día y las más cercanas.

Se pregunta de una de dos maneras, nunca las dos juntas:

  • fecha: el día del calendario real. La 00:15 del sábado se pide como fecha = sábado, aunque pertenezca al turno del viernes.
  • jornada (versión 1.10): la noche a la que pertenece la hora, como la dice el cliente. "El viernes a las 2 de la mañana" se pide como jornada = viernes y hora = 02:00; la API lo traduce al calendario y responde con la fecha real (el sábado). Así no tienen que hacer ustedes la traducción.

En los dos casos, el bloque jornada de la respuesta habla de la noche a la que pertenece la hora consultada, no del día del calendario: preguntando por el sábado a las 02:00, el texto dice "El viernes…".

La hora de cierre del turno se puede reservar. En un turno de 23:00 a 01:00, la 01:00 es una hora válida (el hasta es inclusivo). La primera hora que ya no entra es la siguiente, la 01:15.

asistencia_minima indica cuántas personas del grupo deben llegar para respetar la mesa (null si el lugar no tiene esa regla).

Áreas (versión 1.7): con area_id solo cuenta el lugar en esa área, también para las alternativas. areas_con_lugar dice qué áreas tienen lugar a esa hora cuando no hay lugar o cuando se pidió un área (para ofrecer otra si la pedida está llena); si hay lugar y no se pidió área viene null. Cuando hay lugar, ocupacion trae ultimos_lugares y un texto para leer ("Quedan pocos lugares a esa hora").

Authorizations:
bearerAuthapiKey
query Parameters
restaurante_id
required
integer >= 1
Example: restaurante_id=88
fecha
string <date>
Example: fecha=2026-09-18

AAAA-MM-DD del calendario, en la zona horaria del restaurante. Obligatoria salvo que se mande jornada; las dos juntas responden 422 parametros_invalidos.

jornada
string <date>
Example: jornada=2026-09-18

AAAA-MM-DD de la noche a la que pertenece la hora (versión 1.10), para preguntar como habla el cliente: jornada=2026-09-18&hora=02:00 es "el viernes a las 2 de la mañana" y la API consulta el sábado 19 a las 02:00. Para una hora que no es de madrugada (más de las 05:45) es lo mismo que fecha.

hora
required
string^([01]\d|2[0-3]):(00|15|30|45)$
Example: hora=23:00

HH:MM en 24 h, en cuartos de hora.

personas
required
integer [ 1 .. 500 ]
Example: personas=2
area_id
integer >= 1
Example: area_id=5

Opcional: id de areas[] de la ficha (terraza, salón…). Sin él, cualquier área.

Responses

Response samples

Content type
application/json
Example
{
  • "alternativas": [ ],
  • "asistencia_minima": null,
  • "disponible": true,
  • "fecha": "2026-09-18",
  • "hora": "23:00",
  • "horario_del_dia": [ ],
  • "jornada": null,
  • "mensaje": null,
  • "motivo": null,
  • "personas": 2,
  • "area_id": null,
  • "restaurante_id": 88,
  • "zona_horaria": "America/Mexico_City",
  • "areas_con_lugar": null,
  • "ocupacion": {
    },
  • "eventos": [
    ],
  • "experiencias": [ ]
}

Reservaciones

Crear, consultar, modificar y cancelar las reservaciones creadas por el mismo token.

Crear reservación

Revisa la disponibilidad con las mismas reglas que GET /disponibilidad y:

  • hay lugar → 201 confirmada, con mesa asignada;
  • el lugar no reserva en línea → 201 pendiente de confirmación;
  • lleno o cerrado → 409 sin_lugar con alternativas, o 201 pendiente si se envía si_no_hay_lugar: pendiente;
  • grupo fuera de rango u hora sin anticipación → siempre 409;
  • area_id que no es un área de ese restaurante → 422 area_invalida (tampoco queda pendiente).

Con area_id la mesa se busca solo en esa área; sin él, en cualquiera, como el portal. La respuesta trae area con el área de la mesa asignada (en una pendiente, la que se pidió).

Enviar dos veces el mismo id_externo (misma llamada: mismo restaurante y teléfono) devuelve la misma reservación con 200 y repetida: true. Si ese id_externo ya se usó con otro restaurante o teléfono → 409 id_externo_en_uso. Requiere el permiso reservas.crear.

Authorizations:
bearerAuthapiKey
Request Body schema: application/json
required
restaurante_id
required
integer >= 1
fecha
required
string <date>

Fecha real (AAAA-MM-DD) en la zona horaria del restaurante.

hora
required
string^([01]\d|2[0-3]):(00|15|30|45)$
personas
required
integer [ 1 .. 500 ]
area_id
integer or null >= 1

Opcional: id de areas[] de la ficha. La mesa se busca solo en esa área; sin él, en cualquiera (como el portal).

nombre
required
string <= 128 characters
apellido
required
string <= 128 characters
telefono
required
string

Sin pais: 10 dígitos de México, como siempre (acepta espacios, guiones, paréntesis y lada +52). Desde la 1.11, de cualquier país: con pais ("212 555 1234" y pais: "US") o con su lada ("+34 612 345 678"; la lada escrita manda sobre pais). Se guarda el número nacional, sin lada.

pais
string or null^[A-Za-z]{2}$

Opcional (1.11): país del teléfono en ISO 3166 de 2 letras (MX, US, ES…). Sin él, México. Uno que no existe → 422 en campos.pais.

telefono_2
string or null

Opcional (1.12): segundo teléfono, misma regla que telefono (con pais_2 o su lada). Vacío o null = sin segundo teléfono. Inválido → 422 en campos.telefono_2.

pais_2
string or null^[A-Za-z]{2}$

Opcional (1.12): país del segundo teléfono (ISO de 2 letras). Sin él, México.

correo
string or null <email>
fecha_nacimiento
string or null <date>

Opcional: cumpleaños del cliente (AAAA-MM-DD); se guarda en su ficha del restaurante. Desde la 1.12 no puede ser futura ni anterior a 1900.

ocasion
string or null

Texto libre; se agrega a la nota que ve el restaurante. Desde la 1.13 también se traduce a una ocasión del catálogo (cumple → Cumpleaños, aniversario, negocio, cita, amigos, despedida; otro texto → Otra) si no viene ocasion_id.

ocasion_id
integer or null >= 1

(1.13) Ocasión del catálogo (GET /ocasiones): 2 Cumpleaños, 3 Aniversario… Manda sobre el texto ocasion. Con Cumpleaños la respuesta trae promociones_aplicables. Una que no existe → 422 en campos.ocasion_id.

experiencia_id
integer or null >= 1

(1.14) Experiencia de experiencias[] de la ficha. Debe aplicar esa noche, a esa hora, para esas personas y con cupo; si no, 422 experiencia_no_aplica con motivo y nada se crea. Se paga en el lugar.

festejado
string or null <= 128 characters

Opcional (1.12): de quién es el festejo, si no es de quien reserva. Va a la nota: "Cumpleaños de: Luis" (o "Festejado: Luis" si la ocasión no es un cumpleaños). Desde la 1.13 también se guarda aparte y se devuelve en festejado.

nombre_festejado
string or null

Igual que festejado (nombre de la Etapa B, sigue funcionando). Si llegan los dos, manda festejado.

peticion_especial
string or null <= 200 characters
origen
required
string
Enum: "bot" "agente"
id_externo
required
string <= 100 characters

Id de la llamada en su sistema. Repetirlo devuelve la misma reservación, no crea otra.

si_no_hay_lugar
string
Default: "rechazar"
Enum: "rechazar" "pendiente"

Si está lleno o cerrado, pendiente registra la solicitud para que el restaurante la confirme.

modo_prueba
boolean
Default: false

Valida todo y responde como si reservara, pero no guarda nada ni avisa.

Responses

Request samples

Content type
application/json
Example
{
  • "restaurante_id": 88,
  • "fecha": "2026-09-18",
  • "hora": "22:30",
  • "personas": 2,
  • "area_id": 181,
  • "nombre": "Prueba",
  • "apellido": "CallMarket",
  • "telefono": "55 0000 0001",
  • "correo": "prueba.callmarket@masmesa.test",
  • "ocasion": "Aniversario",
  • "peticion_especial": "Mesa cerca del piano",
  • "origen": "bot",
  • "id_externo": "llamada-2026-09-18-000123",
  • "si_no_hay_lugar": "rechazar",
  • "modo_prueba": false
}

Response samples

Content type
application/json
{
  • "folio": "684108",
  • "estado": "confirmada",
  • "mensaje_para_cliente": "Tu reservación está confirmada.",
  • "correo_enviado": false,
  • "restaurante_id": 88,
  • "fecha": "2026-09-18",
  • "hora": "22:30",
  • "personas": 2,
  • "jornada": {
    },
  • "area": {
    },
  • "cliente": {
    },
  • "ocasion": null,
  • "festejado": null,
  • "nota": "Ocasión: Aniversario · Mesa cerca del piano",
  • "origen": "agente",
  • "id_externo": "ejemplo-docs-confirmada",
  • "repetida": true,
  • "modo_prueba": false
}

Reservaciones vigentes de un teléfono

Reservaciones de hoy en adelante creadas por este mismo token para ese teléfono. Requiere el permiso reservas.leer_propias.

Authorizations:
bearerAuthapiKey
query Parameters
telefono
required
string
Example: telefono=55 0000 0001

10 dígitos de México (acepta espacios y lada +52); de otro país, con pais o con su lada (1.11).

pais
string^[A-Za-z]{2}$
Example: pais=US

Opcional (1.11): país del teléfono, ISO de 2 letras (MX, US, ES…). Sin él, México. También se puede escribir la lada en telefono ("+1 212 555 1234").

Responses

Response samples

Content type
application/json
{
  • "total": 2,
  • "reservaciones": [
    ]
}

Consultar una reservación por folio

"¿Quedó mi reserva?". El folio solo no basta: el teléfono debe coincidir, para que dictar un número al azar no revele datos de otra persona. El estado se lee en vivo de resbok. Solo devuelve reservaciones creadas por este mismo token. Requiere el permiso reservas.leer_propias.

Authorizations:
bearerAuthapiKey
path Parameters
folio
required
string^[1-9]\d{5}$
Example: 482913
query Parameters
telefono
required
string
Example: telefono=55 0000 0001

10 dígitos de México (acepta espacios y lada +52); de otro país, con pais o con su lada (1.11).

pais
string^[A-Za-z]{2}$
Example: pais=US

Opcional (1.11): país del teléfono, ISO de 2 letras (MX, US, ES…). Sin él, México. También se puede escribir la lada en telefono ("+1 212 555 1234").

Responses

Response samples

Content type
application/json
Example
{
  • "folio": "684108",
  • "estado": "confirmada",
  • "mensaje_para_cliente": "Tu reservación está confirmada.",
  • "correo_enviado": false,
  • "restaurante_id": 88,
  • "fecha": "2026-09-18",
  • "hora": "22:30",
  • "personas": 2,
  • "jornada": {
    },
  • "area": {
    },
  • "cliente": {
    },
  • "ocasion": null,
  • "festejado": null,
  • "nota": "Ocasión: Aniversario · Mesa cerca del piano",
  • "origen": "agente",
  • "id_externo": "ejemplo-docs-confirmada",
  • "repetida": false,
  • "modo_prueba": false
}

Modificar fecha, hora, personas o área

Servicio a clientes: "¿puedo cambiar mi reserva?". Folio + teléfono, como en la consulta. También se acepta PUT.

  • Confirmada: solo cambia si hay lugar en el nuevo horario (su propia mesa no estorba). Si no hay, responde 409 sin_lugar con alternativas y conserva su horario actual.
  • Pendiente de confirmación: cambia de horario y sigue pendiente; el restaurante la confirmará.
  • Siempre aplican las reglas del restaurante (máximo y mínimo de personas, anticipación).
  • Área (area_id, versión 1.7): busca mesa solo en esa área. Sin area_id la mesa nueva puede ser de cualquier área. Un área que no es de ese restaurante → 422 area_invalida.
  • No se puede cambiar si está cancelada, ya se usó o su hora ya pasó → 409 no_modificable.
  • Mandar el mismo horario que ya tiene responde 200 con repetida: true y no cambia nada.

Requiere el permiso reservas.modificar.

Authorizations:
bearerAuthapiKey
path Parameters
folio
required
string^[1-9]\d{5}$
Example: 204065
Request Body schema: application/json
required
telefono
required
string

El de la reservación (10 dígitos de México; acepta espacios y lada +52). Otro país, con pais o su lada (1.11).

pais
string^[A-Za-z]{2}$

Opcional (1.11): país del teléfono (ISO de 2 letras), el mismo con que se creó. Sin él, México.

origen
required
string
Enum: "bot" "agente"

Quién hace el cambio; queda en la bitácora.

fecha
string <date>
hora
string^([01]\d|2[0-3]):(00|15|30|45)$
personas
integer [ 1 .. 500 ]
area_id
integer >= 1

id de areas[] de la ficha: busca mesa solo en esa área.

telefono_2
string or null

(1.12) Segundo teléfono nuevo (con pais_2 o su lada). Vacío o null lo borra. Se puede mandar solo.

pais_2
string or null^[A-Za-z]{2}$

(1.12) País del segundo teléfono (ISO de 2 letras). Sin él, México.

ocasion_id
integer or null >= 1

(1.13) Nueva ocasión del catálogo (GET /ocasiones). Una que no existe → 422 en campos.ocasion_id.

ocasion
string or null

(1.13) Igual que ocasion_id, dicha con texto (se traduce al catálogo).

festejado
string or null <= 128 characters

(1.13) Nuevo festejado; vacío o null lo borra.

Responses

Request samples

Content type
application/json
Example
{
  • "telefono": "55 0000 0003",
  • "origen": "agente",
  • "hora": "22:30",
  • "personas": 3
}

Response samples

Content type
application/json
Example
{
  • "folio": "204065",
  • "estado": "confirmada",
  • "mensaje_para_cliente": "Tu reservación está confirmada.",
  • "correo_enviado": false,
  • "restaurante_id": 88,
  • "fecha": "2026-09-18",
  • "hora": "22:30",
  • "personas": 3,
  • "jornada": {
    },
  • "area": {
    },
  • "cliente": {
    },
  • "ocasion": null,
  • "festejado": null,
  • "nota": null,
  • "origen": "bot",
  • "id_externo": "ejemplo-docs-cambios-111238",
  • "repetida": false,
  • "modo_prueba": false
}

Cancelar

Cancela la reservación y libera su lugar; el restaurante recibe el aviso. Los datos van en la URL (para clientes que no mandan cuerpo en DELETE); también se aceptan en un cuerpo JSON. Cancelar una reservación ya cancelada responde 200 con repetida: true. No se puede cancelar si ya se usó o su hora ya pasó → 409 no_modificable.

Requiere el permiso reservas.cancelar.

Authorizations:
bearerAuthapiKey
path Parameters
folio
required
string^[1-9]\d{5}$
Example: 204065
query Parameters
telefono
required
string
Example: telefono=55 0000 0003

El de la reservación (10 dígitos; acepta espacios y lada +52).

pais
string^[A-Za-z]{2}$
Example: pais=US

Opcional (1.11): país del teléfono, ISO de 2 letras (MX, US, ES…). Sin él, México. También se puede escribir la lada en telefono ("+1 212 555 1234").

origen
required
string
Enum: "bot" "agente"
motivo
string <= 200 characters
Example: motivo=Cambio de planes

Máximo 200 caracteres; queda en la bitácora.

Responses

Response samples

Content type
application/json
Example
{
  • "folio": "204065",
  • "estado": "cancelada",
  • "mensaje_para_cliente": "Esta reservación está cancelada.",
  • "correo_enviado": false,
  • "restaurante_id": 88,
  • "fecha": "2026-09-18",
  • "hora": "22:30",
  • "personas": 3,
  • "jornada": {
    },
  • "area": {
    },
  • "cliente": {
    },
  • "ocasion": null,
  • "festejado": null,
  • "nota": null,
  • "origen": "bot",
  • "id_externo": "ejemplo-docs-cambios-111238",
  • "repetida": false,
  • "modo_prueba": false
}

Registrar la respuesta del cliente sobre su asistencia (1.16)

En el seguimiento del día siguiente por WhatsApp, el cliente contesta si fue o no fue. resbok la combina con lo que marcó la hostess (regla del 19-sep-2026): basta un "sí" de cualquiera de los dos para que cuente como asistencia; queda como "no llegó" solo si la hostess lo marcó y el cliente no dijo que sí, o si el cliente dijo que no y la hostess no marcó nada. Se acepta desde la hora de la reservación y hasta 48 h después; pasado el plazo no cambia nada. La respuesta se guarda siempre, aunque no cambie la reservación. El folio puede ser de una reservación de este token o de cualquier canal (el de los avisos). Requiere el permiso reservas.modificar.

Authorizations:
bearerAuthapiKey
path Parameters
folio
required
string^[1-9]\d{5}$
Example: 482913
Request Body schema: application/json
required
asistio
required
boolean

true = sí fui, false = no fui

respondido_en
string

Hora de la respuesta del cliente, Y-m-d H:i:s (opcional; si falta, ahora).

Responses

Request samples

Content type
application/json
{
  • "asistio": true,
  • "respondido_en": "2026-09-19 10:32:00"
}

Response samples

Content type
application/json
{
  • "folio": "482913",
  • "respuesta": "no_fui",
  • "resultado": "aplicada",
  • "estado": "no_llego",
  • "marcado_por": "cliente"
}

Nivel alto

Todas las reservaciones de resbok, de cualquier canal (solo lectura, permiso reservas.leer_todas).

Agenda de un restaurante por día

Todas las reservaciones de resbok de ese restaurante en un día (hostess, portal, web, teléfono, Google, CallMarket…), no solo las creadas por este token. La madrugada aparece en su día real. No incluye reservaciones a medio crear ni de pruebas.

Solo lectura: modificar y cancelar sigue siendo solo para reservaciones con folio de este token. Trae datos de clientes: requiere el permiso reservas.leer_todas, que resbok otorga aparte.

Authorizations:
bearerAuthapiKey
path Parameters
id
required
integer >= 1
Example: 88

Id del restaurante en resbok.

query Parameters
fecha
required
string <date>
Example: fecha=2026-09-18

Día real (AAAA-MM-DD) en la zona horaria del restaurante.

pagina
integer >= 1
Default: 1
por_pagina
integer [ 1 .. 100 ]
Default: 50

Responses

Response samples

Content type
application/json
{
  • "total": 8,
  • "pagina": 1,
  • "por_pagina": 2,
  • "hay_mas": true,
  • "reservaciones": [
    ]
}

Rastrear reservaciones por teléfono

Reservaciones de hoy en adelante (hora de México) con ese teléfono, en cualquier restaurante habilitado y por cualquier canal. Compara los últimos 10 dígitos: encuentra el número aunque se haya guardado con lada o espacios. Máximo 50, ordenadas por fecha y hora.

Requiere el permiso reservas.leer_todas.

Authorizations:
bearerAuthapiKey
query Parameters
telefono
required
string
Example: telefono=55 0000 0003

10 dígitos de México (acepta espacios, guiones, paréntesis y lada +52); de otro país, con pais o con su lada (1.11).

pais
string^[A-Za-z]{2}$
Example: pais=US

Opcional (1.11): país del teléfono, ISO de 2 letras (MX, US, ES…). Sin él, México. También se puede escribir la lada en telefono ("+1 212 555 1234").

Responses

Response samples

Content type
application/json
{
  • "total": 1,
  • "reservaciones": [
    ]
}

Avisos (webhooks)

resbok le avisa a su sistema cuando una reservación nace o cambia de estado, en los restaurantes habilitados. Se configura por integración (URL https://…, qué eventos y un secreto que resbok genera y enseña una sola vez).

Cómo llega: POST a su URL con Content-Type: application/json, User-Agent: resbok-Webhook/1.0 y la cabecera X-Firma: sha256=<HMAC-SHA256 en hexadecimal del cuerpo exacto, con su secreto>.

Qué contestar: un 2xx en menos de 10 segundos. Si no, se reintenta a 1, 2, 4, 8… minutos (tope una hora) hasta 12 veces; después queda marcado para revisión y se puede reintentar a mano.

Comprobar la firma: firmen el cuerpo tal cual llegó (sin volver a armar el JSON) y compárenlo con la cabecera. En PHP: hash_equals('sha256=' . hash_hmac('sha256', $cuerpoCrudo, $secreto), $_SERVER['HTTP_X_FIRMA']).

Las reservaciones que creó la propia integración no le llegan como nueva (ya las conoce); sus cambios sí.

Aviso de prueba: al configurar el webhook se puede mandar uno con "folio": "PRUEBA", "estado": "prueba" y "evento": "prueba", firmado igual, para comprobar la firma antes de encender los eventos.

Una reservación nació o cambió de estado Webhook

Eventos que se pueden encender por integración: reserva.nueva, reserva.confirmada, reserva.pendiente, reserva.cancelada, reserva.asistio y reserva.no_asistio. El cuerpo dice cuál fue con evento + estado.

header Parameters
X-Firma
required
string
Example: sha256=5d41402abc4b2a76b9719d911017c592ae2f1b6a4a1b0c0e7f1d3c5b7a9e0f12

sha256= seguido del HMAC-SHA256 (hexadecimal) del cuerpo exacto, con el secreto de la integración.

Request Body schema: application/json
required
folio
required
string

Folio de 6 dígitos de la reservación (el mismo de GET /reservaciones/{folio}).

estado
required
string
Enum: "confirmada" "pendiente_confirmacion" "cancelada" "asistio" "no_asistio"
fecha
required
string <date>

Fecha del calendario (una reservación de la 1 a. m. del sábado dice sábado).

hora
required
string
personas
required
integer
nombre
required
string or null
apellido
required
string or null
telefono
required
string or null

Número nacional, sin lada.

correo
required
string or null
restaurante_id
required
integer
required
object
origen
required
string
Value: "masmesa"

Valor fijo que identifica a resbok como quien manda el aviso (se conserva por compatibilidad).

evento
required
string
Enum: "nueva" "cambio"

nueva: la reservación acaba de nacer. cambio: cambió de estado.

canal
required
string
Enum: "hostess" "portal" "telefono" "web" "rp" "whatsapp" "google" "widget" "callmarket" "otro"

Por dónde entró la reservación; el mismo catálogo de la agenda.

pais
required
string or null

País del teléfono (ISO de 2 letras).

telefono_internacional
required
string or null
marcado_por
string
Enum: "restaurante" "cliente" "sistema"

Solo en asistio / no_asistio. restaurante: lo marcó la hostess. cliente: lo decidió la respuesta del cliente en el seguimiento. sistema: resbok lo marcó sola porque nadie tocó la reservación en 48 h; un asistio del sistema no prueba que el cliente fue.

Responses

Request samples

Content type
application/json
Example
{
  • "folio": "632544",
  • "estado": "confirmada",
  • "fecha": "2026-10-03",
  • "hora": "21:30",
  • "personas": 4,
  • "nombre": "Ana",
  • "apellido": "López",
  • "telefono": "5512345678",
  • "correo": "ana@example.com",
  • "restaurante_id": 88,
  • "jornada": {
    },
  • "origen": "masmesa",
  • "evento": "nueva",
  • "canal": "portal",
  • "pais": "MX",
  • "telefono_internacional": "+525512345678"
}