Desarrolla con GuestMaker
Documentación de la API
Autentica tu integración, sincroniza datos de huéspedes y reservas y recibe actualizaciones mediante webhooks.
En esta página
Inicio rápido
Crea una clave de API, elige tu hotel y envía tu primera solicitud.
Obtén tus credenciales de API
Solicita acceso a la API mediante nuestro formulario para socios o contacta con tu responsable de cuenta. Recibirás:
- Una clave de API que empieza por
gmkr_ - Los ID de tus hoteles desde Ajustes → Hoteles
- Documentación de los permisos disponibles
Realiza tu primera llamada a la API
Crea una reserva con los datos del huésped. Esta llamada crea la reserva, vincula los huéspedes a un hotel y activa recorridos automáticos.
curl -X POST https://www.guestmaker.ai/api/v1/reservations \
-H "Authorization: Bearer gmkr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"reservation_id": "RES-2026-001234",
"confirmation_number": "CONF-ABC123",
"hotel_name": "Grand Hotel",
"check_in": "2026-03-15",
"check_out": "2026-03-18",
"status": "confirmed",
"source": "pms",
"amount": 450.00,
"amount_net": 371.90,
"currency": "EUR",
"channel": "booking.com",
"market_segment": "LEISURE",
"agency_code": "AGY-001",
"external_created_at": "2026-01-20T14:30:00+01:00",
"external_updated_at": "2026-02-05T09:15:00+01:00",
"guests": [
{
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com",
"phone": "+1234567890",
"is_holder": true,
"pax_type": "adult",
"opt_in_status": "opted_in",
"nationality": "US",
"language": "en",
"document_type": "passport",
"document_number": "AB1234567"
},
{
"first_name": "Jane",
"last_name": "Doe",
"pax_type": "adult"
}
],
"stays": [
{
"start_date": "2026-03-15",
"end_date": "2026-03-18",
"room_type": "Deluxe Suite",
"room_number": "301",
"board_type": "BB",
"adults": 2
}
]
}'Comprueba la respuesta
Una respuesta correcta incluye el ID del contacto y el ID del evento:
{
"success": true,
"data": {
"reservation_id": "550e8400-e29b-41d4-a716-446655440002",
"contact_id": "550e8400-e29b-41d4-a716-446655440000",
"hotel_id": "550e8400-e29b-41d4-a716-446655440001",
"is_new_contact": true,
"event_id": "550e8400-e29b-41d4-a716-446655440003",
"message": "Reservation created successfully"
}
}Configura webhooks (opcional)
Configura endpoints de webhook en tu panel para recibir avisos en tiempo real cuando los huéspedes respondan o terminen los recorridos.
Explora toda la API
Sigue leyendo para conocer los eventos, webhooks, gestión de errores y buenas prácticas para integraciones en producción.
Autenticación
Las solicitudes de la API principal v1 requieren autenticación con un token Bearer en la cabecera Authorization.
Authorization: Bearer gmkr_your_api_keyFormato de la clave de API
Las claves de la API principal tienen el prefijo gmkr_. Recibes tu clave de API al configurar la integración con nosotros.
Permisos disponibles
| Alcance | Descripción |
|---|---|
guests:read | Leer datos de huéspedes y contactos |
guests:write | Crear o actualizar huéspedes con reservas |
events:write | Enviar eventos para activar recorridos |
bookings:read | Leer datos de reservas |
reservations:write | Crear o actualizar reservas mediante API |
conversions:write | Informar de conversiones de reservas para atribuirlas a campañas |
custom_fields:write | Registrar y leer definiciones de campos personalizados |
webhooks:manage | Configurar webhooks salientes |
cdp:read | Leer perfiles y datos de reservas de CDP |
cdp:write | Incorporar reservas a la plataforma de datos de clientes |
b2b:read | Leer cuentas y oportunidades B2B |
b2b:write | Crear o actualizar cuentas, contactos y oportunidades B2B |
surveys:write | Enviar respuestas de encuestas antiguas de Hotelinking a Encuestas |
* | Acceso completo (todos los permisos) |
Buenas prácticas de seguridad
- Nunca expongas una clave de API secreta en código del cliente. La excepción es una clave pública de navegador (
gme_pk_…) para eventos web: está diseñada para incluirse en una página y solo funciona desde los sitios web que permitas. - Guarda tu clave de API en variables de entorno
- Rota tu clave de API periódicamente
- Usa listas de IP permitidas cuando sea posible
- Solicita solo los permisos que necesites
Protege tu clave de API
Tu clave de API da acceso a los datos de tu cuenta. Trátala como una contraseña y nunca la compartas públicamente.
API de reservas
Crea y actualiza reservas desde tu PMS o motor de reservas. Admite varios huéspedes por reserva, estancias por habitación y extras. Diseñada para integraciones empresariales de PMS con sincronización completa de datos.
Permiso obligatorio
Cuerpo de la solicitud
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
reservation_id | string | Obligatorio | ID único de reserva de tu PMS (se usa para identificar la reserva al insertar o actualizar) |
confirmation_number | string | Opcional | Código de confirmación o localizador (puede compartirse entre reservas) |
booking_id | string | Opcional | ID de reserva principal (para agrupar varias reservas) |
hotel_id | string | Opcional | UUID del hotel (se requiere hotel_id, hotel_code o hotel_name) |
hotel_code | string | Opcional | Código del hotel (alternativa a hotel_id) |
hotel_name | string | Opcional | Nombre del hotel (alternativa a hotel_id) |
check_in | string | Obligatorio | Fecha de entrada (YYYY-MM-DD) |
check_out | string | Obligatorio | Fecha de salida (YYYY-MM-DD) |
booking_date | string | Opcional | Fecha en que se hizo la reserva (YYYY-MM-DD) |
status | string | Opcional | Estado de reserva: confirmed, pending (motor de reservas a la espera de confirmación de pago), modified, cancelled, no_show, checked_in, checked_out |
channel | string | Opcional | Canal de reserva (p. ej., booking.com, expedia, direct) |
source | string | Opcional | Tipo de sistema que envió los datos: pms, booking_engine, crm, channel_manager, website, etc. Por defecto, "api" |
company | string | Opcional | Empresa de la reserva corporativa |
amount | number | Opcional | Importe total de la reserva (bruto) |
amount_net | number | Opcional | Importe total de la reserva (neto, antes de impuestos) |
currency | string | Opcional | Código de moneda ISO 4217 (p. ej., EUR, USD) |
market_segment | string | Opcional | Código de segmento de mercado del PMS (p. ej., "LEISURE", "CORPORATE") |
agency | string | Opcional | Nombre de la agencia, turoperador u OTA que vende (p. ej., "BOOKING.COM", "EASYJET HOLIDAYS"). Máx. 200 caracteres |
agency_code | string | Opcional | Código de agencia de viajes del PMS, cuando la fuente lo envía. Máx. 100 caracteres |
nights | number | Opcional | Número de noches (se acepta, pero no se guarda: se calcula automáticamente a partir de las fechas) |
external_created_at | string | Opcional | Marca de tiempo de creación en el PMS (ISO 8601 con zona horaria) |
external_updated_at | string | Opcional | Marca de tiempo de última actualización en el PMS (ISO 8601 con zona horaria) |
guests | array | Obligatorio | Array de huéspedes (1-20). Consulta el objeto Guest más abajo. |
stays | array | Opcional | Array de estancias por habitación (máx. 10). Consulta el objeto Stay más abajo. |
extras | array | Opcional | Array de extras y cargos (máx. 100). Consulta el objeto Extra más abajo. |
comments | string | Opcional | Notas internas o solicitudes especiales |
metadata | object | Opcional | Datos personalizados de clave y valor |
Objeto Guest
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
first_name | string | Obligatorio | Nombre del huésped |
last_name | string | Opcional | Apellidos del huésped |
email | string | Opcional | Dirección de correo electrónico del huésped. Se requiere email o phone para crear o identificar un contacto. |
phone | string | Opcional | Número de teléfono en formato E.164. Se requiere email o phone para crear o identificar un contacto. |
is_holder | boolean | Opcional | Si es el huésped principal o quien hace la reserva |
pax_type | string | Opcional | Tipo de pasajero: adult, child, infant |
date_of_birth | string | Opcional | Fecha de nacimiento (YYYY-MM-DD) |
gender | string | Opcional | Género del huésped: male, female, other, prefer_not_to_say |
nationality | string | Opcional | Código de país ISO 3166-1 alpha-2 |
document_type | string | Opcional | Tipo de documento de identidad: passport, national_id, drivers_license, other |
document_number | string | Opcional | Número del documento de identidad (máx. 50 caracteres) |
address | object | Opcional | Dirección postal del huésped. Todos los subcampos son opcionales: street (máx. 300), city (200), postal_code (40), province (200), state (200), country (ISO 3166-1 alpha-2 o alpha-3, normalizado a alpha-2). Los valores largos se truncan y los no reconocidos se descartan: una dirección mal formada nunca provoca el rechazo de la reserva. Los subcampos omitidos se mantienen sin cambios, en lugar de borrarse. En España, province es el código INE de dos dígitos ("07" para Baleares). Aquí country es el país de RESIDENCIA, no la nacionalidad. |
email_consent | boolean | Opcional | Consentimiento de marketing por correo electrónico comunicado por el PMS. Tiene prioridad sobre la configuración de consentimiento automático de CDP. |
whatsapp_marketing_consent | boolean | Opcional | Consentimiento de marketing por WhatsApp o teléfono comunicado por el PMS. Tiene prioridad sobre la configuración de consentimiento automático de CDP. |
language | string | Opcional | Código de idioma del huésped (p. ej., "en", "es", "fr"). Por defecto, "en" para contactos nuevos. |
pre_checkin_completed_at | string | Opcional | Marca de tiempo ISO 8601 (con desplazamiento horario) en que este huésped completó el registro previo u online en el portal del PMS o motor de reservas. Omítela o envía null si aún no lo ha completado. |
pre_checkin_source | string | Opcional | Dónde se completó el registro previo (p. ej., "mews", "cloudbeds", "apaleo", "self_service"). Máx. 50 caracteres. |
Objeto Stay
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
start_date | string | Obligatorio | Fecha de inicio de estancia (YYYY-MM-DD) |
end_date | string | Obligatorio | Fecha de fin de estancia (YYYY-MM-DD) |
room_type | string | Opcional | Tipo o categoría de habitación |
room_number | string | Opcional | Número de habitación asignada |
board_type | string | Opcional | Régimen: RO (solo alojamiento), BB (alojamiento y desayuno), HB (media pensión), FB (pensión completa), AI (todo incluido) |
rate_code | string | Opcional | Código o plan de tarifa |
adults | number | Opcional | Número de adultos |
children | number | Opcional | Número de niños |
babies | number | Opcional | Número de bebés |
Objeto Extra
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
name | string | Obligatorio | Descripción del cargo |
amount | number | Obligatorio | Importe del cargo |
code | string | Opcional | Código del cargo del PMS |
quantity | number | Opcional | Cantidad (por defecto: 1) |
date | string | Opcional | Fecha del cargo (YYYY-MM-DD) |
notes | string | Opcional | Notas adicionales |
Ejemplo de solicitud
curl -X POST https://www.guestmaker.ai/api/v1/reservations \
-H "Authorization: Bearer gmkr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"reservation_id": "RES-2026-001234",
"confirmation_number": "CONF-ABC123",
"hotel_name": "Grand Hotel",
"check_in": "2026-03-15",
"check_out": "2026-03-18",
"status": "confirmed",
"source": "pms",
"amount": 450.00,
"amount_net": 371.90,
"currency": "EUR",
"channel": "booking.com",
"market_segment": "LEISURE",
"agency_code": "AGY-001",
"external_created_at": "2026-01-20T14:30:00+01:00",
"external_updated_at": "2026-02-05T09:15:00+01:00",
"guests": [
{
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com",
"phone": "+1234567890",
"is_holder": true,
"pax_type": "adult",
"opt_in_status": "opted_in",
"nationality": "US",
"language": "en",
"document_type": "passport",
"document_number": "AB1234567"
},
{
"first_name": "Jane",
"last_name": "Doe",
"pax_type": "adult"
}
],
"stays": [
{
"start_date": "2026-03-15",
"end_date": "2026-03-18",
"room_type": "Deluxe Suite",
"room_number": "301",
"board_type": "BB",
"adults": 2
}
]
}'Respuesta
{
"success": true,
"data": {
"reservation_id": "550e8400-e29b-41d4-a716-446655440000",
"external_reservation_id": "RES-2026-001234",
"is_new": true,
"hotel_id": "550e8400-e29b-41d4-a716-446655440001",
"contacts": [
{
"contact_id": "550e8400-e29b-41d4-a716-446655440002",
"phone": "+1234567890",
"email": "john@example.com",
"is_new": true,
"is_holder": true
},
{
"contact_id": null,
"phone": null,
"email": null,
"is_new": false,
"is_holder": false
}
],
"warnings": []
}
}Creación de contactos
Los huéspedes con un teléfono válido se crean como contactos en GuestMaker. Los huéspedes sin teléfono se guardan en la reserva, pero no pueden recibir mensajes de WhatsApp. La respuesta indica qué huéspedes se han creado como contactos mediante contact_id.
Comportamiento de inserción y actualización
El campo reservation_id es el identificador único de tu PMS. Enviar de nuevo el mismo reservation_id actualiza la reserva existente sin duplicarla. Sincroniza desde tu PMS sin llevar un registro de las reservas ya existentes.
| Estado | Descripción | Etapa del huésped |
|---|---|---|
confirmed | Reserva confirmada | Antes de la llegada |
pending | Motor de reservas a la espera de confirmación de pago | Antes de la llegada |
modified | Reserva modificada tras la confirmación | Antes de la llegada |
checked_in | El huésped ha llegado | Alojado |
checked_out | El huésped ha salido | Después de la estancia |
cancelled | Reserva cancelada | No aplicable |
no_show | El huésped no se ha presentado | No aplicable |
API de huéspedes
Crea y gestiona contactos de huéspedes con sus datos de reserva. Se vinculan automáticamente a hoteles para conversaciones con IA y automatización de recorridos.
Cuerpo de la solicitud
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
phone | string | Obligatorio | Número de teléfono en formato E.164 (p. ej., +1234567890) |
first_name | string | Obligatorio | Nombre del huésped |
last_name | string | Obligatorio | Apellidos del huésped |
email | string | Opcional | Dirección de correo electrónico del huésped |
language | string | Opcional | Código de idioma preferido (p. ej., en, es, fr). Por defecto: en |
tags | string[] | Opcional | Array de etiquetas para segmentación |
custom_fields | object | Opcional | Pares de clave y valor que coincidan con tus definiciones registradas de campos personalizados. Consulta la configuración en la API de campos personalizados. |
hotel_name | string | Opcional | Nombre del hotel al que vincular el huésped |
hotel_code | string | Opcional | Código del hotel (alternativa a hotel_name) |
source | string | Opcional | Tipo de sistema que envió los datos: pms, booking_engine, crm, channel_manager, website, etc. Por defecto, "api" |
booking | object | Opcional | Datos de la reserva (ver más abajo) |
trigger_event | boolean | Opcional | Activar el evento booking.created (por defecto: true) |
update_if_exists | boolean | Opcional | Actualizar el contacto existente por teléfono (por defecto: true) |
opt_in_status | string | Opcional | Campo antiguo (obsoleto). Usa whatsapp_marketing_consent. |
whatsapp_marketing_consent | boolean | Opcional | Consentimiento de marketing por WhatsApp (por defecto: false). Si es true, el huésped recibe mensajes de marketing por WhatsApp. |
email_consent | boolean | Opcional | Consentimiento de marketing por correo electrónico (por defecto: false). Si es true, el huésped recibe correos de marketing. |
date_of_birth | string | Opcional | Fecha de nacimiento del huésped (YYYY-MM-DD) |
gender | string | Opcional | Género del huésped: male, female, other, prefer_not_to_say |
nationality | string | Opcional | Código de país ISO 3166-1 alpha-2 (p. ej., US, ES, GB) |
document_type | string | Opcional | Tipo de documento de identidad: passport, national_id, drivers_license, other |
document_number | string | Opcional | Número del documento de identidad (máx. 50 caracteres) |
address | object | Opcional | Dirección postal del huésped. Todos los subcampos son opcionales: street (máx. 300), city (200), postal_code (40), province (200), state (200), country (ISO 3166-1 alpha-2 o alpha-3, normalizado a alpha-2). Los valores largos se truncan y los no reconocidos se descartan: una dirección mal formada nunca provoca el rechazo de la solicitud. Los subcampos omitidos se mantienen sin cambios, en lugar de borrarse. En España, province es el código INE de dos dígitos ("07" para Baleares). Aquí country es el país de RESIDENCIA, no la nacionalidad. |
subscription_status | string | Opcional | Estado de suscripción a comunicaciones: active, unsubscribed |
communication_preferences | object | Opcional | Preferencias de comunicación detalladas (ver más abajo) |
consent_source | string | Opcional | Tu identificador del lugar donde se obtuvo el consentimiento |
consent_timestamp | string | Opcional | Marca de tiempo ISO 8601 de cuándo se dio el consentimiento |
Objeto Booking
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
booking_id | string | Obligatorio | ID de reserva principal (agrupa varias reservas, p. ej., una reserva de Expedia con 4 habitaciones) |
reservation_id | string | Opcional | Código de reserva o localizador individual (por defecto, booking_id si no se indica) |
check_in | string | Obligatorio | Fecha de entrada (YYYY-MM-DD) |
check_out | string | Obligatorio | Fecha de salida (YYYY-MM-DD) |
room_type | string | Opcional | Tipo o categoría de habitación |
room_number | string | Opcional | Número de habitación asignada (si se conoce) |
rate_plan | string | Opcional | Nombre del plan de tarifa |
board_type | string | Opcional | Régimen: RO (solo alojamiento), BB (alojamiento y desayuno), HB (media pensión), FB (pensión completa), AI (todo incluido) |
booking_channel | string | Opcional | Código de canal original del PMS (p. ej., BDC, EXP). Se guarda como booking_channel_code. Se resuelve automáticamente a un nombre legible y tipo de canal (direct/ota/tour_operator) si la cuenta tiene asignaciones configuradas. |
total_amount | number | Opcional | Importe total de la reserva |
currency | string | Opcional | Código de moneda (por defecto: EUR) |
status | string | Opcional | Confirmed, Modified, CheckedIn, CheckedOut, Cancelled, NoShow |
guests | array | Opcional | Huéspedes adicionales de la reserva (consulta Huéspedes de la reserva más abajo) |
extras | array | Opcional | Extras de la reserva, como spa, minibar o restaurante (consulta Extras de la reserva más abajo) |
Array de huéspedes de la reserva
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
first_name | string | Obligatorio | Nombre del huésped |
last_name | string | Opcional | Apellidos del huésped |
email | string | Opcional | Dirección de correo electrónico del huésped |
is_holder | boolean | Opcional | Si este huésped es el titular de la reserva (por defecto: false) |
pax_type | string | Opcional | Adult, Child o Infant |
relationship_to_holder | string | Opcional | Spouse, Child, Colleague, Friend, etc. |
pre_checkin_completed_at | string | Opcional | Marca de tiempo ISO 8601 (con desplazamiento horario) en que este acompañante completó el registro previo u online. |
pre_checkin_source | string | Opcional | Dónde se completó el registro previo (p. ej., "mews", "cloudbeds", "self_service"). Máx. 50 caracteres. |
Array de extras de la reserva
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
name | string | Obligatorio | Nombre del extra (p. ej., servicios de spa, minibar, restaurante) |
amount | number | Obligatorio | Importe cobrado |
quantity | number | Opcional | Cantidad (por defecto: 1) |
notes | string | Opcional | Notas o descripción adicionales |
Objeto de preferencias de comunicación (antiguo)
Preferencias de comunicación antiguas. Es preferible usar los campos booleanos whatsapp_marketing_consent y email_consent directamente en el objeto del huésped.
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
marketing | boolean | Opcional | Consentimiento para recibir mensajes de marketing (promociones, ofertas). Por defecto: false. Se asigna a whatsapp_marketing_consent. |
utility | boolean | Opcional | Los mensajes de utilidad (confirmaciones de reserva, recordatorios) siempre se entregan. Este campo se ignora. |
Nota: Cuando un huésped se da de baja mediante el chat de WhatsApp, puede dejar de recibir todas las comunicaciones o solo los mensajes de marketing, manteniendo los de utilidad.
Ejemplo de solicitud
curl -X POST https://www.guestmaker.ai/api/v1/guests \
-H "Authorization: Bearer gmkr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"phone": "+1234567890",
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com",
"nationality": "US",
"document_type": "passport",
"document_number": "AB1234567",
"source": "pms",
"hotel_name": "Grand Hotel",
"booking": {
"booking_id": "BK-12345",
"check_in": "2026-03-15",
"check_out": "2026-03-18",
"room_type": "Deluxe Suite",
"total_amount": 450.00,
"status": "Confirmed"
}
}'Respuesta
{
"success": true,
"data": {
"contact_id": "550e8400-e29b-41d4-a716-446655440000",
"is_new": true,
"hotel_id": "550e8400-e29b-41d4-a716-446655440001",
"reservation_id": "550e8400-e29b-41d4-a716-446655440002",
"event_id": "550e8400-e29b-41d4-a716-446655440003",
"message": "Guest created successfully"
}
}API de eventos
Envía eventos para activar recorridos automáticos. Se comparan con los activadores de los recorridos activos para iniciar conversaciones automatizadas de WhatsApp.
Cuerpo de la solicitud
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
event_type | string | Obligatorio | Identificador del tipo de evento (p. ej., guest.checked_in) |
payload | object | Obligatorio | Datos del evento disponibles en el contexto del recorrido |
contact_id | string | Opcional | UUID del contacto (si se conoce) |
guest_phone | string | Opcional | Número de teléfono para identificar al contacto (E.164) |
hotel_id | string | Opcional | UUID del hotel (si se conoce) |
hotel_name | string | Opcional | Nombre del hotel para resolver hotel_id |
hotel_code | string | Opcional | Código del hotel para resolver hotel_id |
idempotency_key | string | Opcional | Clave única para evitar eventos duplicados |
source | string | Opcional | Identificador del sistema de origen para seguimiento |
Tipos de evento estándar
El sistema reconoce estos tipos de evento estándar. También puedes crear tipos personalizados.
booking.createdNueva reserva recibida
booking.updatedReserva modificada
booking.cancelledReserva cancelada
guest.checked_inHuésped llegado
guest.checked_outHuésped salido
guest.messageEl huésped ha enviado un mensaje
payment.receivedPago confirmado
review.requestedReseña solicitada
Los tipos de evento personalizados deben seguir el patrón category.action (por ejemplo, spa.appointment_booked)
Ejemplo de solicitud
curl -X POST https://www.guestmaker.ai/api/v1/events \
-H "Authorization: Bearer gmkr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"event_type": "guest.checked_in",
"guest_phone": "+1234567890",
"hotel_name": "Grand Hotel",
"payload": {
"room_number": "301",
"checked_in_at": "2026-03-15T14:30:00Z"
},
"idempotency_key": "checkin-BK-12345"
}'Respuesta
{
"success": true,
"data": {
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "accepted",
"contact_id": "550e8400-e29b-41d4-a716-446655440001",
"hotel_id": "550e8400-e29b-41d4-a716-446655440002",
"matching_journeys": 2,
"created_at": "2026-03-15T14:30:00Z",
"message": "Event accepted and queued for processing"
}
}Idempotencia
Incluye siempre un idempotency_key para evitar eventos duplicados. Si envías la misma clave de idempotencia dos veces, la segunda solicitud devuelve el evento original.
API de campos personalizados
Define campos personalizados para ampliar los perfiles de huéspedes con datos de tu integración. Estos campos aparecen en el editor de segmentos, para que los equipos del hotel creen segmentos específicos a partir de tus datos.
Permiso obligatorio
Cuerpo de la solicitud
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
fields | array | Obligatorio | Array de definiciones de campos (máx. 50) |
fields[].field_key | string | Obligatorio | Clave única (minúsculas, a-z, 0-9, guiones bajos; debe empezar por una letra) |
fields[].field_label | string | Obligatorio | Etiqueta visible para el personal del hotel |
fields[].field_type | string | Obligatorio | Uno de: string, text, number, boolean, date, datetime, select, multiselect |
fields[].options | string[] | Opcional | Obligatorio para tipos select/multiselect |
fields[].description | string | Opcional | Texto de ayuda para el personal del hotel |
fields[].is_required | boolean | Opcional | Si el campo debe tener un valor (por defecto: false) |
fields[].is_visible | boolean | Opcional | Mostrar en la interfaz de datos del contacto (por defecto: true) |
fields[].display_order | number | Opcional | Orden dentro de tus campos (por defecto: 0) |
Ejemplo de solicitud
curl -X POST https://www.guestmaker.ai/api/v1/fields \
-H "Authorization: Bearer gmkr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"fields": [
{
"field_key": "loyalty_tier",
"field_label": "Loyalty Tier",
"field_type": "select",
"options": ["Bronze", "Silver", "Gold", "Platinum"],
"description": "Guest loyalty program tier"
},
{
"field_key": "loyalty_points",
"field_label": "Loyalty Points",
"field_type": "number",
"description": "Current loyalty point balance"
},
{
"field_key": "member_since",
"field_label": "Member Since",
"field_type": "date"
}
]
}'Respuesta
{
"success": true,
"data": {
"fields": [
{ "field_key": "loyalty_tier", "field_label": "Loyalty Tier", "field_type": "select", "created_at": "2026-01-31T10:00:00Z", "updated_at": "2026-01-31T10:00:00Z" },
{ "field_key": "loyalty_points", "field_label": "Loyalty Points", "field_type": "number", "created_at": "2026-01-31T10:00:00Z", "updated_at": "2026-01-31T10:00:00Z" },
{ "field_key": "member_since", "field_label": "Member Since", "field_type": "date", "created_at": "2026-01-31T10:00:00Z", "updated_at": "2026-01-31T10:00:00Z" }
],
"message": "3 field definition(s) created/updated successfully"
}
}Integración con segmentos
Los campos personalizados establecidos mediante la API de huéspedes (objeto custom_fields) se almacenan automáticamente según las definiciones de campo correspondientes. El personal del hotel puede usarlos en el editor de segmentos, en la categoría «Campos personalizados», para crear audiencias específicas.
| Tipo | Descripción | Valor de ejemplo |
|---|---|---|
string | Texto corto (una línea) | "John Doe" |
text | Texto largo (varias líneas) | "Special dietary requirements..." |
number | Valor numérico | 1500 |
boolean | True/false | true |
date | Fecha (YYYY-MM-DD) | "2026-03-15" |
datetime | Fecha y hora (ISO 8601) | "2026-03-15T14:30:00Z" |
select | Una opción de la lista | "Gold" |
multiselect | Varias opciones de la lista | ["spa", "golf"] |
API de CDP
La API de la plataforma de datos de clientes permite seguir visitantes anónimos, resolver identidades e incorporar reservas tras completar la compra. Sigue a los visitantes del sitio web, vincúlalos a identidades conocidas e incorpora datos de reservas para consolidar perfiles en tu grupo hotelero.
SDK GuestMaker.js
Para el seguimiento web, usa el SDK GuestMaker.js, que gestiona los ID de visitante, los lotes de eventos y el seguimiento automático de páginas vistas. Consulta las instrucciones de configuración en la página de SDK.
Restricciones de dominio
Si hay restricciones de dominio en los ajustes de CDP, el endpoint de eventos valida la cabecera Origin frente a la lista de dominios permitidos. Las solicitudes de orígenes no permitidos se rechazan con un error 403.
Permiso obligatorio
Requiere activar el seguimiento de visitantes en los ajustes de CDP.
Cuerpo de la solicitud
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
visitor_id | string | Obligatorio | Identificador del visitante anónimo (de la cookie del SDK) |
session_id | string | Obligatorio | Identificador de sesión (de sessionStorage del SDK) |
device | object | Opcional | { type: "desktop"|"mobile"|"tablet", browser, os, language } |
utm | object | Opcional | { source, medium, campaign, term, content } |
referrer | string | Opcional | URL de referencia HTTP |
events | array | Obligatorio | Array de objetos de evento (1-100). Consulta el objeto Event más abajo. |
Objeto Event
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
type | string | Obligatorio | Tipo de evento: page_view, scroll, click, form_start, form_submit, identify, custom |
url | string | Opcional | URL de la página (se elimina el dominio en el servidor por privacidad) |
title | string | Opcional | Título de la página |
data | object | Opcional | Datos personalizados del evento (p. ej., profundidad de desplazamiento, elemento pulsado) |
ts | string | Obligatorio | Marca de tiempo ISO 8601 |
Ejemplo de solicitud
curl -X POST https://www.guestmaker.ai/api/v1/cdp/events \
-H "Authorization: Bearer gmkr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"visitor_id": "vis_abc123def456",
"session_id": "sess_789xyz",
"device": {
"type": "desktop",
"browser": "Chrome 120",
"os": "Windows 11",
"language": "en-US"
},
"utm": {
"source": "google",
"medium": "cpc",
"campaign": "summer-2026"
},
"referrer": "https://www.google.com/search?q=luxury+hotels",
"events": [
{
"type": "page_view",
"url": "/rooms/deluxe-suite",
"title": "Deluxe Suite | Grand Hotel",
"ts": "2026-02-19T14:30:00.000Z"
},
{
"type": "scroll",
"url": "/rooms/deluxe-suite",
"data": { "depth": 75 },
"ts": "2026-02-19T14:30:45.000Z"
},
{
"type": "click",
"url": "/rooms/deluxe-suite",
"data": { "element": "book-now-btn", "text": "Book Now" },
"ts": "2026-02-19T14:31:02.000Z"
}
]
}'Respuesta (202 Accepted)
{
"success": true,
"data": {
"accepted": 3
}
}API de fidelización
El programa de fidelización ofrece dos modelos de integración: elige según quién actúa. Tu backend puede actuar en nombre del hotel mediante la API REST, o el huésped puede iniciar sesión con el SSO de fidelización. La mayoría de las integraciones de motores de reservas usan ambos.
API REST de servidor
Tu backend actúa en nombre del hotel con una clave de API. Busca cualquier huésped por correo electrónico; da de alta, concede, canjea y sincroniza puntos y crédito monetario.
Authorization: Bearer <api_key>SSO de fidelización (OIDC)
El huésped inicia sesión con su cuenta de fidelización (OAuth2 + PKCE). Consulta solo su nivel, saldo e historial para personalizar el proceso de reserva.
¿No sabes cuál elegir? Usa la API REST cuando tu servidor actúa en nombre del hotel (sincronización PMS, concesión, canje, crédito monetario). Usa SSO de fidelización cuando un huésped inicia sesión en tu sitio y personalizas para él. El saldo y el historial están disponibles en ambos modelos: mismos datos, distinto límite de confianza.
Parámetros de consulta
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
email | string | Obligatorio | Dirección de correo electrónico del huésped |
Respuesta (socio encontrado)
{
"success": true,
"data": {
"is_member": true,
"member_summary": {
"member_id": "uuid",
"tier_name": "Gold",
"tier_slug": "gold",
"points_balance": 1500,
"lifetime_points": 3000,
"joined_at": "2025-01-15T10:30:00Z"
}
}
}Respuesta (no es socio)
{
"success": true,
"data": {
"is_member": false,
"member_summary": null
}
}guests:writePOST /api/v1/loyalty/actions: concede una bonificación por una acción ajena a la estancia (reseña, encuesta o descarga de la app)POST /api/v1/loyalty/points/confirm: confirma unos puntos pendientes y los incorpora al saldo disponiblePOST /api/v1/loyalty/points/void: anula unos puntos pendientes antes de confirmarlosPOST /api/v1/loyalty/reverse: revierte los puntos ganados ante una cancelación o si el huésped no se presentaPOST /api/v1/loyalty/apply-discount: usa puntos como descuento monetario en una reserva o TPV.reference_ides un metadato, no una clave de reintento; reenviar la solicitud puede descontar puntos de nuevoPOST /api/v1/loyalty/redeem-free-night: canjea puntos por un código de bono de noche gratis. La entrega medianteengine_apino está implementada y devuelve 501
GET /api/v1/loyalty/conversion: calculadora de puntos ↔ dinero, con permisoguests:readGET /api/v1/loyalty/households·POST: saldo compartido del hogar y canje conjuntoGET /api/v1/loyalty/liability: informe de obligaciones por puntos y estado del programa, con permisoguests:readPOST /api/v1/loyalty/experiences/bid: puja con puntos por una experiencia en subasta
Autenticación y permisos
Autentica cada llamada REST con Authorization: Bearer <api_key>: la cuenta se obtiene de la clave y no requiere una cabecera de slug adicional. Los endpoints de lectura (check, member, rewards, tiers, transactions, calculate, conversion, cálculo de crédito monetario e instantánea de reserva) requieren el permiso guests:read; los de escritura (enroll, points, redeem y retención, confirmación, liberación y reversión de crédito monetario) requieren guests:write. Todos comparten el límite de transporte estándar v1 de 2000 solicitudes/minuto, además del límite propio de tu clave (1000/minuto por defecto).
Crédito monetario
Permite a los socios gastar sus puntos como crédito monetario en reservas directas, cargos durante la estancia y al salir. Ciclo de cálculo → retención → confirmación/liberación/caducidad: consulta las páginas detalladas para ver toda la integración.
Diagrama del ciclo, áreas de aplicación, códigos de error y lista de OTA excluidas.
Inserta <script> en 5 minutos, con un callback de aplicación.
/reservation-snapshot + 5 webhooks HMAC para sincronizar saldos en tiempo real.
| Endpoint | Alcance | Comportamiento |
|---|---|---|
GET /api/v1/loyalty/credit/quote | guests:read | Leer saldo disponible y opciones predefinidas |
POST /api/v1/loyalty/credit/hold | guests:write | Reserve points atomically; external_reference_id identifies retries |
POST /api/v1/loyalty/credit/confirm | guests:write | Confirmar el descuento de puntos de una retención |
POST /api/v1/loyalty/credit/release | guests:write | Release a hold; released: true does not prove an active hold was found |
POST /api/v1/loyalty/credit/reverse | guests:write | Return confirmed points after cancellation; reconcile before retrying |
GET /api/v1/loyalty/reservation-snapshot | guests:read | Leer el saldo del socio y el historial de crédito de la reserva |
SSO de fidelización (OIDC)
GuestMaker es un proveedor estándar de identidad OpenID Connect para huéspedes de fidelización. Tu sitio web permite iniciar sesión con la cuenta de fidelización mediante Authorization Code + PKCE y consulta nivel, saldo de puntos e historial para personalizar el proceso de reserva.
El flujo, permisos y declaraciones, requisitos de seguridad obligatorios, modelo de tokens y endpoints.
Guía para copiar y pegar, independiente del framework: URL de autorización, PKCE, intercambio de tokens, verificación de id_token y lectura de datos del socio.
GET /api/oidc/.well-known/openid-configuration: descubrimiento (carga los endpoints y las JWKS desde aquí)GET /api/oidc/auth: autorización (redirección del navegador, PKCE S256)POST /api/oidc/token: intercambio de tokens (client_secret_basic)GET /api/oidc/jwks: claves públicas de firma RS256GET /api/loyalty/me/balance: nivel y puntos del socio (Bearer)GET /api/loyalty/me/transactions: historial de puntos (Bearer)
Webhooks
Recibe avisos en tiempo real cuando ocurran eventos en la plataforma. Configura los endpoints de webhook en los ajustes del panel.
message.receivedMensaje entrantemessage.sentMensaje saliente enviadomessage.deliveredMensaje entregadomessage.readMensaje leído por el destinatariocontact.createdNuevo contacto añadidocontact.updatedDatos del contacto modificadoscontact.deletedContacto eliminadoconversation.createdNueva conversación iniciadaconversation.closedConversación cerradajourney.startedAutomatización del recorrido iniciadajourney.completedAutomatización del recorrido terminadajourney.failedError de ejecución del recorridobroadcast.sentCampaña enviadabroadcast.completedCampaña terminadareservation.checked_inEntrada del huésped (comprobación diaria de fechas: ver nota)reservation.checked_outSalida del huésped (comprobación diaria de fechas: ver nota)Nota: horarios de reservation.checked_in / checked_out. Se activan con una comprobación diaria al llegar la fecha de entrada o salida del huésped; no se activan en tiempo real. Cada entrega incluye un objeto scheduled_for con la hora local prevista de entrada o salida, para programar acciones en tu sistema. Las horas son configurables por cuenta (por defecto: entrada 15:00, salida 12:00 y zona horaria del hotel).
{
"event_type": "contact.created",
"timestamp": "2026-03-15T10:30:00Z",
"data": {
"contact": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"phone": "+1234567890",
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com",
"hotel_id": "550e8400-e29b-41d4-a716-446655440001"
}
}
}Los eventos de reserva incluyen la reserva, el huésped y la indicación scheduled_for:
{
"event_type": "reservation.checked_in",
"timestamp": "2026-07-10T03:12:08Z",
"data": {
"reservation": {
"id": "…", "hotel_id": "…", "status": "CheckedIn",
"check_in": "2026-07-10", "check_out": "2026-07-12",
"localizer_code": "ABC123", "booking_id": "…",
"source": "…", "booking_channel": "…", "total_value": 540, "currency": "EUR"
},
"contact": {
"id": "…", "first_name": "John", "last_name": "Doe",
"email": "john@example.com", "phone": "+34600000000",
"language": "en", "nationality": "GB"
},
"scheduled_for": {
"date": "2026-07-10", "local_time": "15:00",
"timezone": "Europe/Madrid", "iso": "2026-07-10T13:00:00.000Z"
}
}
}| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
X-Webhook-Signature | string | Obligatorio | HMAC-SHA256 signature: sha256=... |
X-Webhook-Event | string | Obligatorio | El tipo de evento que se entrega |
X-Webhook-Delivery | string | Obligatorio | ID único de entrega (UUID) |
X-Webhook-Timestamp | string | Obligatorio | Marca de tiempo ISO 8601 del evento |
const crypto = require('crypto');
// Sign over `timestamp + "." + rawBody` (the timestamp is signed for replay
// resistance). Verify against the RAW request body, never a re-serialized object.
function verifyWebhookSignature(rawBody, timestamp, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
const wanted = Buffer.from(`sha256=${expected}`);
const actual = Buffer.from(signature || '');
return actual.length === wanted.length && crypto.timingSafeEqual(actual, wanted);
}
// Capture the raw body (do NOT re-serialize req.body):
app.use('/webhook', express.raw({ type: 'application/json' }));
app.post('/webhook', (req, res) => {
const rawBody = req.body.toString('utf8');
const timestamp = req.headers['x-webhook-timestamp'];
const signature = req.headers['x-webhook-signature'];
if (!verifyWebhookSignature(rawBody, timestamp, signature, 'your_webhook_secret')) {
return res.status(401).send('Invalid signature');
}
// Retries retain the original event timestamp.
// Use stable payload identifiers for idempotent processing.
const { event_type, data } = JSON.parse(rawBody);
console.log(`Received event: ${event_type}`, data);
res.status(200).send('OK');
});CTI / Telefonía
Conecta tu centro de llamadas a GuestMaker para abrir fichas en llamadas entrantes, llamar con un clic, grabar e incorporar un historial de llamadas por contacto. Todos los proveedores usan el mismo contrato unificado: conecta una vez y tus agentes tendrán la misma experiencia con Ringover u otro proveedor compatible.
Paso 1. En el panel, abre Settings → Integrations → {Provider} y pulsa Conectar.
Paso 2. Pega las credenciales de tu proveedor (clave de API para Ringover, flujo de consentimiento OAuth para Teams). GuestMaker valida las credenciales y devuelve para cada cuenta una URL de webhook y Token de autorización.
Paso 3. En el panel de tu proveedor, configura un webhook con esa URL y token. Activa estos eventos: call_ringing, call_answered, call_hangup, call_missed y record_available.
Paso 4. Pulsa Probar conexión: una llamada entrante simulada abre la ficha en tu panel.
Paso 5. Configura los modos de apertura de ficha (navegación automática, aviso, pestaña nueva) y la posición del widget. Los agentes se vinculan automáticamente por correo electrónico desde la lista de usuarios de tu proveedor.
call_ringingLlamada sonando: activa la búsqueda de contacto y la apertura de fichacall_answeredLlamada atendida por un agentecall_hangupLlamada terminada (incluye duración en segundos)call_missedLlamada sin respuestarecord_availableURL de grabación disponiblecall_completedUn registro después de la llamada (metadatos + transcripción/resumen opcional): consulta Envío de llamadas completadas más abajoPOST /api/webhooks/cti
Authorization: Bearer <your_webhook_token>
Content-Type: application/json
{
"event_type": "call_ringing",
"call_id": "ringover-abc-123",
"user_id": "ringover-user-42",
"from_number": "+34612345678",
"to_number": "+34900111222",
"direction": "inbound"
}Tu token corresponde a una integración, que identifica la cuenta y el proveedor. Por eso esta URL única funciona para todos los proveedores, sin necesidad de un slug de proveedor. La forma específica /api/webhooks/cti/{provider} es equivalente y sigue siendo compatible.
Los nombres de campo se aceptan en snake_case o camelCase, y también pueden estar anidados en un objeto data. Límite por cuenta: 100 solicitudes/minuto.
Envía mediante POST un evento call_completed por llamada terminada. Vuelve a enviar el mismo call_id más adelante para actualizar el registro y adjuntar la transcripción cuando termine. Si hay transcripción, se extrae información para la memoria de IA del huésped.
POST /api/webhooks/cti
Authorization: Bearer <your_webhook_token>
Content-Type: application/json
{
"event": "call_completed",
"call_id": "zn-8f3a2c91",
"direction": "inbound",
"from": "+34600111222",
"to": "+34971000000",
"agent": { "id": "zoon-user-42", "email": "agent@hotel.com" },
"caller": { "name": "María García", "email": "maria@example.com" },
"started_at": "2026-07-16T10:31:04Z",
"answered_at": "2026-07-16T10:31:12Z",
"ended_at": "2026-07-16T10:35:49Z",
"outcome": "completed",
"recording_url": "https://rec.example.com/zn-8f3a2c91.mp3",
"summary": "Guest asked about a late checkout on the 18th.",
"transcript": [
{ "speaker": "guest", "text": "Hola, quería un late checkout.", "offset_ms": 0 },
{ "speaker": "agent", "text": "Claro, dígame su habitación.", "offset_ms": 4200 }
]
}Cuerpo de la solicitud
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
event | string | Obligatorio | El literal "call_completed". |
call_id | string | Obligatorio | Tu ID único estable: la única clave de idempotencia, específica por (cuenta, proveedor). |
direction | string | Obligatorio | inbound o outbound. Omítelo por completo para enviar una actualización de contenido (ver más abajo). |
from / to | string | Obligatorio | Números E.164. El lado del huésped (from en entrantes, to en salientes) identifica o crea un contacto. |
started_at / ended_at | string | Obligatorio | ISO 8601. A full record missing either is dropped; a content patch does not need them. |
answered_at | string | Opcional | ISO 8601. Ausente o null significa que la llamada nunca se atendió. |
duration_seconds | number | Opcional | Se calcula a partir de ended_at − (answered_at o started_at) si se omite. |
agent | object | Opcional | { id, email }: matched to the GuestMaker user who handled the call. An email in either field lets us resolve the user without a manual mapping. |
caller | object | Opcional | { name | first_name/last_name, email }: the guest's known details. Fills blank contact fields only; never overwrites. |
outcome | string | Opcional | completed | missed | no_answer | voicemail | texto libre. missed, no_answer y voicemail guardan la llamada como perdida. |
recording_url | string | Opcional | Se guarda sin cambios y se muestra como enlace de reproducción en el contacto. El audio permanece en tus servidores. |
transcript | array | Opcional | [{ speaker, text, offset_ms? }]: feeds guest-memory extraction. Capped at 500 turns / 200,000 characters; oversized pushes are truncated, not rejected. |
transcript_text | string | Opcional | Alternativa en texto plano. Las líneas con prefijo Guest:/Agent: se separan en turnos si se detectan al menos dos. |
summary | string | Opcional | Tu resumen de IA: se muestra en cursiva bajo la llamada en el historial del contacto. |
language, metadata | string, object | Opcional | Merged into the call's metadata. Existing keys are kept; only keys present in the push are added or overwritten. |
Datos adicionales del llamante. El objeto opcional caller solo rellena los campos vacíos de nombre y correo del contacto identificado; nunca sobrescribe datos existentes. Se rechazan etiquetas del operador ("MOBILE CALLER", "Número privado") y nombres de relleno. Un correo solo se aplica si es válido y no pertenece a otro contacto.
Idempotencia. Volver a enviar el mismo call_id actualiza el registro con la lógica de COALESCE: los campos opcionales que no incluya un reenvío nunca borran contenido guardado. La extracción de memoria del huésped solo se ejecuta la primera vez que llega una transcripción.
Actualizaciones de contenido. La grabación y la transcripción suelen terminar minutos después de la llamada. No necesitas reenviar todo el registro: envía call_id y solo el contenido nuevo, omitiendo direction, from/to y las marcas de tiempo. La actualización combina recording_url, transcript, summary, outcome y caller en la llamada existente sin modificar nada más. Envía tantas actualizaciones como necesites. Solo combinan contenido y nunca crean una llamada; envía primero el registro completo al colgar. Se descarta una actualización si no encuentra un call_id coincidente.
Respuestas. 200 {"received": true} (procesado de forma asíncrona), 401 token incorrecto, 400 JSON inválido, 429 límite superado (las cargas históricas deben espaciarse y respetar los reintentos).
<script src="https://www.guestmaker.ai/cti/sdk/v1.js"></script>
<script>
const gm = await GuestMakerCTI.connect({
hostOrigin: "https://www.guestmaker.ai"
});
gm.agentEmail; // the GuestMaker user signed in right now
gm.locale; // "es"
await gm.openContactByPhone("+34661024890");
gm.on("clickToCall", ({ phone }) => dialer.call(phone));
</script>Métodos (panel → GuestMaker)
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
setPanelMode(mode) | { mode } | Obligatorio | mode es "docked", "collapsed" o "hidden". Devuelve el modo aplicado, que puede ser distinto si el host lo rechaza. |
lookupContact(phone) | { found, contactId?, displayName? } | Obligatorio | Teléfono E.164. Identificación del llamante de solo lectura: indica si existe un contacto y su nombre visible, sin cambiar el panel. Solo el nombre: nunca correo electrónico, reservas ni historial. |
openContactByPhone(phone) | { found, contactId? } | Obligatorio | E.164 phone. Opens the guest's record on a hit; does not navigate on a miss. |
screenPop(params) | { found, contactId?, popped } | Opcional | { phone, callId?, direction?, state? }. Announces a live call in this browser and honours the tenant's screen-pop settings. popped reports whether anything actually happened. |
openContactSearch(query) | { shown } | Opcional | Texto libre, máx. 200 caracteres. Abre la lista de contactos con la búsqueda rellenada. No devuelve registros. |
openCreateContact({ phone }) | { shown } | Opcional | Abre el formulario de creación de contacto rellenado. Una persona debe guardarlo: la llamada no escribe nada. |
Eventos (GuestMaker → panel)
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
clickToCall | { phone, contactId? } | Opcional | Un agente ha pulsado un número de teléfono en el panel. |
panelModeChanged | { mode } | Opcional | El agente ha contraído o restaurado el panel desde nuestra interfaz, para mantener el tuyo sincronizado. |
Códigos de error
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
UNKNOWN_METHOD | host | Opcional | No es un método de esta versión del protocolo. |
INVALID_PARAMS | host | Opcional | Failed validation; the message names the offending field. |
NOT_FOUND | host | Opcional | Solicitud bien formada, sin coincidencias. |
NOT_PERMITTED | host | Opcional | Origen o cuenta sin permiso para realizar esa llamada. |
TIMEOUT | sdk | Opcional | Sin respuesta en 10 segundos. Se genera localmente, no en el host. |
El protocolo es el contrato. El SDK simplifica el uso: puedes implementar los mensajes directamente. Cada mensaje incluye gmcti: 1; una solicitud es { gmcti, id, type: "request", method, params } y la respuesta devuelve tu id sin cambios.
Acciones, sin datos del huésped. Las respuestas incluyen booleanos, identificadores y el estado aplicado; nunca nombres, correos electrónicos, reservas ni historial. Si necesitas datos de contacto en tus sistemas, usa GET /api/v1/guests?phone= de servidor a servidor con una clave guests:read. También funciona sin ninguna pestaña del navegador abierta.
Antes de conectar. Envíanos el origen exacto desde el que se sirve tu panel: lo añadimos a las listas permitidas del intercambio de mensajes y de nuestra CSP. No se carga nada hasta que lo hagamos.
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
auto_navigate | boolean | Opcional | Default true. Current tab navigates to /contacts/{id} on ring. |
notification | boolean | Opcional | Por defecto, true. Aviso deslizante con nombre del llamante y botón Abrir contacto. |
new_tab | boolean | Opcional | Default false. Opens /contacts/{id} in a background tab. |
widget_position | string | Opcional | Posición del widget SDK del proveedor: 'bottom-right' (por defecto) o 'bottom-left'. |
Los números entrantes desconocidos se normalizan a E.164 y se buscan en los contactos de tu cuenta. Si no hay coincidencia, GuestMaker crea un contacto mínimo con source = '{provider}' (por ejemplo, 'ringover'). Se abre la ficha con is_new_contact: true para que el agente vea un registro nuevo.
Nota: los teléfonos se comparan por igualdad exacta en E.164. Los proveedores que envían números nacionales sin código de país pueden generar contactos duplicados. Normaliza antes del envío siempre que sea posible.
Modelos de datos
Referencia de las estructuras de datos usadas en toda la API.
{
"id": "uuid", // Unique identifier
"phone": "+1234567890", // Phone in E.164 format
"phone_normalized": "1234567890", // Normalized phone (digits only)
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com",
"language": "en", // ISO 639-1 language code
"tags": ["vip", "returning"], // Array of tags
"custom_fields": {}, // Custom key-value pairs
"opt_in_status": "opted_in", // Legacy (deprecated). Use consent booleans below.
"subscription_status": "active", // active | unsubscribed (hard block)
"whatsapp_marketing_consent": true, // WhatsApp marketing gate
"email_consent": true, // Email marketing master gate
"guest_stage": "pre_stay", // unknown | pre_stay | during_stay | post_stay
"hotel_id": "uuid", // Linked hotel
"current_reservation_id": "uuid", // Active reservation
"source": "pms", // System type: pms, booking_engine, crm, etc.
// Personal data fields
"date_of_birth": "1985-06-15", // YYYY-MM-DD format
"gender": "male", // male | female | other | prefer_not_to_say
"nationality": "US", // ISO 3166-1 alpha-2 code
"document_type": "passport", // passport | national_id | drivers_license | other
"document_number": "AB1234567", // ID document number
// Consent & subscription fields
"subscription_status": "active", // active | unsubscribed
"opted_out_at": null, // Timestamp when unsubscribed
"consent_source": "api", // Where consent was collected
"consent_partner_id": "uuid", // Integration partner who collected consent
"consent_updated_at": "2024-03-01T10:00:00Z",
"created_at": "2024-03-01T10:00:00Z",
"updated_at": "2024-03-01T10:00:00Z"
}{
"id": "uuid",
"tenant_id": "uuid",
"hotel_id": "uuid",
"localizer_code": "BK-12345", // Your external booking ID
"external_id": "BK-12345", // External system reference
"source": "hotelinking", // Canonical origin (e.g. "neobookings", "roiback", "hotelinking", "manual")
"source_code": "hotelinking", // Raw value sent by producer (preserved for audit)
"source_type": "pms", // pms | booking_engine | manual | api | import
"attribution_source": null, // Marketing attribution: e.g. "google_ads", "meta_ads", "booking_com" (null if not supplied)
"status": "Confirmed", // Confirmed | Modified | CheckedIn | CheckedOut | Cancelled | NoShow
"check_in": "2026-03-15", // Check-in date
"check_out": "2026-03-18", // Check-out date
"nights": 3, // Calculated nights
"room_type": "Deluxe Suite",
"room_number": "405",
"board_type": "BB",
"rate_plan": "Best Available",
"total_value": 450.00,
"currency": "EUR",
"adults": 2,
"children": 0,
"notes": "Late checkout requested",
"created_at": "2024-03-01T10:00:00Z",
"updated_at": "2024-03-01T10:00:00Z"
}unknownSin datos de reserva
pre_stayAntes de la entrada
during_stayEn el hotel
post_stayDespués de la salida
Las etapas del huésped se actualizan automáticamente mediante una tarea cron diaria y al recibir eventos de reserva. El asistente de IA usa la etapa para personalizar las conversaciones.
CRM B2B
Incorpora agencias de viajes, turoperadores y cuentas corporativas como entidades B2B. Las cuentas admiten jerarquía de matriz y filiales (hasta 3 niveles), condiciones comerciales (comisiones, crédito y códigos promocionales) e identificadores que permiten atribuir automáticamente las reservas a cuentas. Los contactos B2B son contactos de huéspedes normales marcados con contact_type: "b2b" y vinculados a cuentas.
Todos los endpoints /api/v1/b2b/* requieren que el módulo de CRM B2B esté activado para tu cuenta. Las solicitudes sin él devuelven 403 MODULE_NOT_ENABLED. Contacta con tu responsable de cuenta para activar el módulo.
Permiso obligatorio
Cuerpo de la solicitud
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
legal_name | string | Obligatorio | Razón social (máx. 200 caracteres) |
trade_name | string | Opcional | Nombre comercial o de marca |
tax_id | string | Opcional | CIF/NIF o identificación fiscal internacional. Único por cuenta (sin distinguir mayúsculas): se usa para identificar registros al insertar o actualizar y en referencias parent_account |
iata_code | string | Opcional | Código IATA de agencia: también identifica coincidencias para atribuir reservas |
sector | string | Opcional | Sector de actividad (texto libre) |
profile | string | Opcional | corporate o mice (por defecto: corporate). Determina el flujo de oportunidades y los bloques de cuenta aplicables |
account_type | string | Opcional | travel_agency, tour_operator, dmc, incentive_agency, corporate_booking, corporate, event_organizer, other |
source | string | Opcional | Procedencia de la cuenta (texto libre) |
website | string | Opcional | URL del sitio web de la empresa |
email_domains | string[] | Opcional | Hasta 20 dominios (p. ej., @acme.com). Los correos de titulares de reserva en estos dominios generan sugerencias de atribución |
billing_address | object | Opcional | Dirección fiscal (consulta el objeto Billing Address más abajo) |
billing_details | object | Opcional | Mapa de cadenas de clave y valor (p. ej., invoice_email, notas de IVA) |
parent_account | object | Opcional | Reference to an existing parent account (see Account Reference Object below). Must resolve (422 PARENT_NOT_FOUND); hierarchy max 3 levels (422 HIERARCHY_TOO_DEEP) |
status | string | Opcional | prospect, active, inactive (por defecto: active) |
promo_codes | string[] | Opcional | Hasta 50 códigos promocionales o de tarifa (p. ej., CORP_ACME2026). Se comparan con los códigos de tarifa de reservas para atribuirlas |
commitment_room_nights | number | Opcional | Compromiso anual contratado de noches de habitación |
contract_start_date | string | Opcional | Inicio del contrato (YYYY-MM-DD) |
contract_end_date | string | Opcional | Fin del contrato (YYYY-MM-DD): activa avisos de renovación |
payment_method | string | Opcional | credit o direct |
credit_limit | number | Opcional | Importe del límite de crédito |
payment_days | number | Opcional | Plazo de pago en días (0–365) |
cancellation_policy | string | Opcional | Política de cancelación acordada (máx. 2000 caracteres) |
commission_rate | number | Opcional | Porcentaje de comisión sobre alojamiento (0–100). Las cuentas filiales lo heredan de la matriz si no está definido |
commission_settlement | string | Opcional | deducted_invoice o post_checkout |
external_id | string | Opcional | Tu identificador de PMS/CRM. Único por cuenta: clave principal para insertar o actualizar cuando se indica |
channel_manager_code | string | Opcional | Código de agencia del channel manager (identificador de coincidencia) |
crs_code | string | Opcional | Código de agencia del CRS (identificador de coincidencia) |
mirai_agency_id | string | Opcional | ID de agencia de Mirai Pro (identificador de coincidencia) |
custom_fields | object | Opcional | Pares libres de clave y valor |
Objeto Billing Address
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
line1 | string | Opcional | Línea de dirección 1 |
line2 | string | Opcional | Línea de dirección 2 |
city | string | Opcional | Ciudad |
region | string | Opcional | Región / estado / provincia |
postal_code | string | Opcional | Código postal |
country | string | Opcional | ISO 3166-1 alpha-2 en mayúsculas (p. ej., ES, US) |
Objeto Account Reference
Lo utiliza parent_account aquí, account en oportunidades y b2b_account al incorporar huéspedes. Se requiere al menos un identificador. Orden de resolución: id → external_id → tax_id → iata_code.
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
id | string | Opcional | UUID de cuenta de GuestMaker |
external_id | string | Opcional | Tu identificador de PMS/CRM |
tax_id | string | Opcional | Identificación fiscal (comparación sin distinguir mayúsculas) |
iata_code | string | Opcional | Código IATA de agencia |
Ejemplo de solicitud
curl -X POST https://www.guestmaker.ai/api/v1/b2b/accounts \
-H "Authorization: Bearer gmkr_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"legal_name": "Viajes Mediterraneo S.L.",
"trade_name": "Viajes Mediterraneo",
"tax_id": "B12345678",
"iata_code": "78212345",
"profile": "corporate",
"account_type": "travel_agency",
"email_domains": ["@viajesmediterraneo.com"],
"billing_address": {
"line1": "Calle Gran Via 28",
"city": "Madrid",
"postal_code": "28013",
"country": "ES"
},
"commission_rate": 10,
"commission_settlement": "post_checkout",
"payment_method": "credit",
"credit_limit": 50000,
"payment_days": 30,
"promo_codes": ["AGMED2026"],
"commitment_room_nights": 1500,
"contract_start_date": "2026-01-01",
"contract_end_date": "2026-12-31",
"parent_account": { "tax_id": "B87654321" },
"external_id": "PMS-AG-0042"
}'Respuesta
201 cuando se ha creado una cuenta nueva; 200 cuando se ha actualizado una existente.
{
"success": true,
"data": {
"account_id": "550e8400-e29b-41d4-a716-446655440000",
"is_new": true,
"message": "Account created successfully"
}
}Cada reserva incorporada se compara con los identificadores de tus cuentas por orden estricto de prioridad. Prevalece la primera coincidencia; nunca se sobrescriben los vínculos manuales del panel.
Código de agencia
El código de agencia o canal de la reserva coincide con iata_code, channel_manager_code, crs_code o external_id de una cuenta: vinculación automática.
Código promocional / de tarifa
El código de tarifa de la estancia coincide con uno de los promo_codes de la cuenta (o de una oportunidad corporativa abierta): vinculación automática.
ID de agencia de Mirai
El identificador de agencia de Mirai del origen de la reserva coincide con mirai_agency_id: vinculación automática.
Dominio de correo electrónico (solo sugerencia)
El dominio del correo del titular aparece en email_domains de una cuenta: se muestra como sugerencia para confirmación humana, nunca se vincula automáticamente.
Cuantos más identificadores envíes en las cuentas (IATA, códigos de channel manager, códigos promocionales, dominios de correo), mayor será la cobertura de atribución automática.
Cuerpo de solicitud o parámetros de consulta inválidos
VALIDATION_ERRORClave de API inválida o ausente
UNAUTHORIZEDEl módulo de CRM B2B no está activado para esta cuenta (también FORBIDDEN si la clave no tiene el permiso b2b)
MODULE_NOT_ENABLEDEl ID de cuenta no existe para esta cuenta de cliente
NOT_FOUNDLa referencia parent_account no identifica una cuenta existente
PARENT_NOT_FOUNDVincular la matriz superaría los 3 niveles de jerarquía de cuentas
HIERARCHY_TOO_DEEPVincular la matriz crearía un ciclo en la jerarquía
HIERARCHY_CYCLEOportunidades: la referencia de cuenta no identifica una cuenta existente
ACCOUNT_NOT_FOUNDOportunidades: no existe un pipeline para el tipo de flujo (no se han creado los valores predeterminados del módulo)
NO_PIPELINELímite de solicitudes superado
RATE_LIMITEDGestión de errores
{
"success": false,
"error": {
"message": "Validation failed",
"code": "VALIDATION_ERROR",
"details": [
{ "field": "phone", "message": "Phone number is required" },
{ "field": "first_name", "message": "First name is required" }
]
}
}Solicitud correcta
Recurso creado correctamente
Solicitud aceptada para procesamiento asíncrono
Parámetros de solicitud inválidos
VALIDATION_ERRORClave de API inválida o ausente
UNAUTHORIZEDPermisos insuficientes
FORBIDDENRecurso no encontrado
NOT_FOUNDEl recurso ya existe
CONFLICTSe entiende la solicitud, pero no puede procesarse
UNPROCESSABLELímite de solicitudes superado
RATE_LIMITEDError del servidor
INTERNAL_ERRORServicio temporalmente no disponible
SERVICE_UNAVAILABLELímites de solicitudes
Las solicitudes de API tienen límites por clave de API. El límite predeterminado es 1,000 solicitudes por minuto.
Cabeceras de límite de solicitudes
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
X-RateLimit-Limit | number | Obligatorio | Máximo de solicitudes por minuto |
X-RateLimit-Remaining | number | Obligatorio | Solicitudes restantes en el intervalo actual |
X-RateLimit-Reset | number | Obligatorio | Marca de tiempo Unix en que se restablece el límite |
Respuesta de límite de solicitudes
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1710507600
Retry-After: 45
{
"success": false,
"error": {
"message": "Rate limit exceeded. Please retry after 45 seconds.",
"code": "RATE_LIMITED"
}
}¿Necesitas límites más altos?
Contacta con nosotros para hablar de límites más altos para tu integración. Dependen de la clave de API y del endpoint; las rutas principales v1 también aplican un control por IP de 2,000 solicitudes/minuto. Los eventos web tienen sus propios límites por fuente.
Buenas prácticas
Para POST /api/v1/events, incluye un valor estable de idempotency_key al reintentar el mismo evento. Los demás endpoints tienen sus propias reglas de reintento e idempotencia.
- Usa una clave única y determinista (por ejemplo,
checkin-{booking_id}) - Los eventos principales comparan la clave con eventos almacenados; no existe una ventana universal de 24 horas
- Un evento principal duplicado devuelve su ID con
status: "duplicate"
Gestiona estos casos de fallo en tu integración:
- Reintenta los errores 5xx transitorios con espera progresiva según las reglas de idempotencia del endpoint
- Ante un HTTP 429, respeta Retry-After. Corrige los demás errores 4xx antes de reintentar
- Registra las respuestas de error para depurar
- Revisa los códigos de error y los resultados de cada elemento del lote; los formatos de respuesta varían por familia de API
- Elige tiempos de espera adecuados para el endpoint y evita envíos duplicados concurrentes
Asegura la fiabilidad de tus endpoints de webhook:
- Confirma cuanto antes los payloads verificados con una respuesta 2xx
- Procesa los webhooks de forma asíncrona si es necesario
- Configura un secreto de firma de webhook y verifica la firma antes de procesar
- Implementa procesamiento idempotente (los webhooks pueden entregarse más de una vez)
- Usa identificadores estables del payload para procesar de forma idempotente.
X-Webhook-Deliveryidentifica un intento
Usa siempre el formato E.164 para teléfonos:
- Empieza por + y código de país (p. ej., +1 para EE. UU., +34 para España)
- Sin espacios, guiones ni paréntesis
- Codifica el + como %2B en los parámetros de consulta de la URL
- Ejemplo:
+34612345678(España)
Usa nuestro sandbox para desarrollar y probar:
- Usa el modo simulado para respuestas de ejemplo. El modo real envía solicitudes reales con tu clave de API
- Prueba todos los escenarios de error (validación, autenticación, límites)
- Usa el sandbox interactivo en /developers/sandbox
- Prueba la verificación de firmas de webhook en local
Flujo de integración recomendado
- Registra las definiciones de campos personalizados mediante
POST /api/v1/fields(una vez) - Envía datos de huéspedes y reservas mediante
POST /api/v1/guests - Activa eventos del ciclo mediante
POST /api/v1/events
Concurrencia y rendimiento
- Envía hasta 10 solicitudes concurrentes para obtener el mejor rendimiento
- Límite predeterminado: 1000 solicitudes/min (contacta con nosotros para límites más altos)
- Supervisa
X-RateLimit-Remainingpara respetar los límites - Implementa espera exponencial ante respuestas
429(2 s, 4 s, 8 s y 16 s)
Deduplicación e idempotencia
- El endpoint de huéspedes usa deduplicación por teléfono: enviar el mismo número actualiza el contacto existente
- Establece
update_if_exists: true(por defecto) para insertar o actualizar de forma segura - La deduplicación de reservas se limita al hotel y prueba primero
reservation_id(guardado comoexternal_id); si no, usabooking_id. Envía el mismoreservation_idpara actualizar una línea de habitación y un valor compartido debooking_idpara agrupar las habitaciones de una misma reserva - El endpoint de eventos usa
idempotency_keypara deduplicar; sigue el contrato de reintento documentado del endpoint
Consejos de rendimiento
- Usa
hotel_codeen lugar dehotel_namepara buscar hoteles más rápido - Registra las definiciones de campos personalizados antes de enviar los datos del huésped
- Incluye
custom_fieldsen la solicitud de huésped para rellenar tanto el almacenamiento JSONB como el editor de segmentos - Los datos de reserva activan automáticamente el cálculo de etapa del huésped y los eventos de recorrido
Migración inicial de datos
Para migraciones iniciales de más de 100K contactos, contacta con nuestro equipo para aumentar temporalmente los límites y acordar una ventana de incorporación. Podemos supervisar el proceso en tiempo real para garantizar la integridad de los datos.