Actualizado
La API REST pública de Nowistay conecta tus scripts y las integraciones de partners con tu cuenta. Puedes leer y actualizar los ajustes de los alojamientos, los calendarios, las reservas y las misiones; gestionar fichas de huéspedes, conversaciones, conocimiento privado y guías de bienvenida; solicitar pagos de reservas y reembolsos por cancelación; y leer upsells, compras e informes de cartera. Consulta los esquemas exactos de solicitudes y respuestas en /public/docs y /public/openapi.json.
Esta guía te presenta tres caminos: la forma más rápida de probar la API (el playground de la documentación interactiva), la forma más sencilla de ejecutar tus propios scripts (una credencial de API personal creada en la aplicación) y el flujo OAuth estándar que usarás al crear una aplicación para otros anfitriones.
El acceso se comprueba según la titularidad y el derecho de asistente del alojamiento. Se necesita una conexión PMS activa para las operaciones que dependen de ella, como la gestión del calendario y la creación de reservas directas. Los demás recursos no necesitan una conexión PMS solo para leerse o editarse.
https://api.nowistay.com/public/v1/* requieren un token de acceso OAuth2, enviado como Authorization: Bearer ....Abre https://api.nowistay.com/public/docs en tu navegador. La página es una interfaz Swagger UI completa de la API, con un playground OAuth integrado que registra un cliente por ti automáticamente. No hace falta programar nada para probar llamadas.
Cmd+Shift+R en Mac, Ctrl+F5 en Windows)./public/docs, ya autenticado.GET /public/v1/properties, haz clic en Try it out (Probar) y después en Execute (Ejecutar).A partir de ahí puedes probar cada endpoint con un clic, ver el JSON exacto que devuelve Nowistay y copiar el comando cURL de cualquier llamada. Es la forma recomendada de explorar la API antes de escribir código.
Importante. El cliente del playground vive solo en tu navegador. Sirve para probar, pero para una integración real debes crear una credencial de API personal o registrar tu propio cliente OAuth (ambas opciones se explican más abajo).
Si automatizas tu propia cuenta (un script de informes, una sincronización con tu herramienta de contabilidad, un pequeño panel), no necesitas el flujo OAuth completo. Crea una credencial de API personal en la aplicación e intercámbiala por un token en una sola llamada. Sin pantalla de consentimiento y sin refresh tokens que gestionar.
Reporting script) y marca solo los permisos que necesita.Tu script intercambia entonces la credencial por un token de acceso (válido durante una hora) y llama a la API:
curl -X POST https://api.nowistay.com/public/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=client_credentials" \
--data-urlencode "client_id=YOUR_CLIENT_ID" \
--data-urlencode "client_secret=YOUR_CLIENT_SECRET"
Cuando el token caduca, solicita uno nuevo del mismo modo. En este flujo no hay refresh token, y esa es la idea: dos líneas de código, nada que vigilar.
Puedes crear hasta 10 credenciales, cada una con su propio nombre y sus permisos. Una credencial solo lee y escribe los datos a los que puede acceder tu propia cuenta, y necesita la misma suscripción activa de AI Channel Manager que el resto de la API.
No puedes editar los scopes de un token ni de una credencial personal existentes. Crea una credencial de sustitución con el conjunto completo de permisos que necesita tu integración. Los clientes, tokens de acceso, códigos de autorización y refresh tokens existentes conservan sus permisos originales; no se añade ningún permiso automáticamente.
https://app.nowistay.com/hub/user/developer. Anota los permisos que usa la integración actual.calendar.write; enviar mensajes necesita messages.write; solicitar el pago de una reserva necesita bookings.write y payments.write.POST /public/oauth/token con grant_type=client_credentials y las credenciales de sustitución. Comprueba el valor scope devuelto y prueba un endpoint permitido por esos scopes. GET /public/v1/properties requiere específicamente properties.read.Rotar clave no añade scopes. Solo sustituye la clave secreta de la misma credencial; la clave anterior sigue siendo válida durante 7 días. Actualizar o solicitar otro token de acceso no puede ampliar el permiso original. Los scopes de escritura no incluyen automáticamente los de lectura.
POST /public/oauth/register con el conjunto completo de scopes deseado y los datos habituales de tu aplicación. Un registro equivalente con exactamente el mismo conjunto de scopes reutiliza el cliente correspondiente; si cambia el conjunto de scopes, se crea un nuevo registro. Guarda las credenciales de cliente devueltas.En el playground de la documentación, actualiza la página y vuelve a autorizar con los scopes necesarios. Los nuevos permisos se muestran con etiquetas legibles y sus identificadores en inglés, francés, español, alemán, neerlandés e italiano.
La API usa el estándar OAuth 2.1 con el flujo de código de autorización y PKCE. En una frase: el usuario abre una página de Nowistay, dice que sí, tú recibes un código corto, lo intercambias por un token de acceso y llamas a la API con ese token. Los tokens caducan y pueden renovarse sin volver a molestar al usuario.
Los tres endpoints OAuth que vas a usar:
/public/oauth/register (POST)/public/oauth/authorize (GET, redirección del navegador)/public/oauth/token (POST)También puedes obtener los metadatos de OAuth en /.well-known/oauth-authorization-server/public-api, que enumera todos los endpoints y las funciones compatibles.
Cualquiera puede registrar un cliente OAuth, así que los anfitriones necesitan una forma de distinguir una aplicación recién creada de una integración consolidada. Por eso toda aplicación recién registrada empieza como no verificada:
¿Estás creando algo para más de 3 cuentas? Escribe a soporte con el nombre de tu aplicación, tu sitio web y tu caso de uso. Cuando tu aplicación esté verificada, el aviso desaparece y se levanta el límite de cuentas.
La API REST admite 25 scopes, incluidos diez permisos adicionales para ediciones de alojamientos, mensajes, guías, conocimiento, creación de huéspedes, upsells y pagos. Selecciona solo los permisos que necesita tu integración.
properties.read: leer los detalles y los ajustes operativos de los alojamientos.properties.write: actualizar los detalles y los ajustes operativos de los alojamientos.calendar.read: leer la disponibilidad, las tarifas por noche y las restricciones de estancia.calendar.write: actualizar la disponibilidad, las tarifas por noche y las restricciones de estancia.bookings.read: leer reservas, detalles financieros y el estado seguro de las solicitudes de pago.bookings.sensitive.read: incluir en las respuestas de reservas los datos de contacto del huésped, las direcciones postales y los códigos de acceso; para las lecturas requiere también bookings.read.bookings.write: crear y actualizar reservas directas. Las solicitudes de pago y los reembolsos requieren además payments.write.booking_parameters.read: leer las definiciones y los valores de los parámetros de reserva personalizados.booking_parameters.write: crear y actualizar las definiciones y los valores de los parámetros.missions.read: leer misiones e informes de finalización.missions.write: crear, actualizar y asignar misiones.activity.read: leer los registros de actividad de los alojamientos.team_members.read: leer miembros del equipo, datos de contacto y asignaciones a alojamientos.webhooks.read: leer las suscripciones a webhooks.webhooks.write: crear, actualizar, pausar y eliminar suscripciones a webhooks.customers.read: leer fichas de huéspedes, datos de contacto e historial de estancias.customers.write: crear fichas de huéspedes.upsells.read: leer el catálogo de upsells y las compras de las reservas.payments.write: solicitar el pago durante la creación de una reserva y ordenar reembolsos totales o parciales por cancelación; requiere además bookings.write.messages.read: leer conversaciones y mensajes con huéspedes.messages.write: enviar mensajes a los huéspedes.guides.read: leer guías de bienvenida y el contenido de las páginas.guides.write: editar guías, crear y actualizar páginas, cambiar la posición de las páginas y subir imágenes.knowledge.read: leer preguntas y respuestas privadas de los alojamientos.knowledge.write: crear y actualizar el conocimiento privado. Copiar requiere además knowledge.read.Los ocho scopes REST predeterminados siguen siendo properties.read, calendar.read, bookings.read, booking_parameters.read, missions.read, activity.read, team_members.read y webhooks.read. Los valores predeterminados no amplían los tokens existentes.
bookings.write + payments.write. La cancelación sin reembolso usa únicamente bookings.write.customers.read + bookings.read.upsells.read + bookings.read. Para leer el catálogo basta upsells.read.knowledge.read + knowledge.write.bookings.write + booking_parameters.write. Un PATCH solo de parámetros mantiene booking_parameters.write y también funciona en reservas de OTA.Los nuevos endpoints de escritura devuelven resultados de acción mínimos, salvo que el token tenga también el scope de lectura correspondiente. Se mantiene la redacción actual de las reservas. Los endpoints de misiones y conversaciones incluyen el contexto de reserva relacionado según sus propios scopes.
Estos ejemplos usan IDs y credenciales de ejemplo. Sustitúyelos por tus propios valores. Este flujo es para aplicaciones de partners; las credenciales personales usan client_credentials, como se ha visto antes.
Llama a POST /public/oauth/register con los permisos que necesitas. Guarda de forma segura el client_id y el client_secret devueltos. El ejemplo solicita lecturas de alojamientos, reservas y misiones.
curl -X POST https://api.nowistay.com/public/oauth/register \
-H "Content-Type: application/json" \
-d '{"clientName":"My integration","redirectUris":["https://my-app.example.com/oauth/callback"],"grantTypes":["authorization_code","refresh_token"],"responseTypes":["code"],"tokenEndpointAuthMethod":"client_secret_post","scope":"properties.read bookings.read missions.read"}'
Para un cliente público sin backend, usa "tokenEndpointAuthMethod": "none" y PKCE, sin client secret.
Genera un verifier de PKCE, su challenge S256 y un state aleatorio. Redirige al usuario al endpoint de autorización. La siguiente URL va en una sola línea; codifica en URL los valores de la consulta.
https://api.nowistay.com/public/oauth/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=https%3A%2F%2Fmy-app.example.com%2Foauth%2Fcallback&scope=properties.read%20bookings.read%20missions.read&state=RANDOM_STATE&code_challenge=BASE64URL_S256_HASH&code_challenge_method=S256
El anfitrión inicia sesión, revisa los permisos y aprueba o deniega el acceso. Si aprueba, el callback contiene un code de un solo uso y tu state. Rechaza cualquier callback cuyo state no coincida.
https://my-app.example.com/oauth/callback?code=AUTHORIZATION_CODE&state=RANDOM_STATE
curl -X POST https://api.nowistay.com/public/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "code=AUTHORIZATION_CODE" \
--data-urlencode "redirect_uri=https://my-app.example.com/oauth/callback" \
--data-urlencode "client_id=YOUR_CLIENT_ID" \
--data-urlencode "client_secret=YOUR_CLIENT_SECRET" \
--data-urlencode "code_verifier=ORIGINAL_PKCE_VERIFIER"
La respuesta incluye access_token, token_type, expires_in, refresh_token y scope. Guarda los tokens de forma segura. Los tokens de acceso suelen durar una hora; usa la caducidad devuelta.
Este ejemplo requiere properties.read. Lee los campos data y pagination de la respuesta.
curl "https://api.nowistay.com/public/v1/properties" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
curl -X POST https://api.nowistay.com/public/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=refresh_token" \
--data-urlencode "refresh_token=YOUR_REFRESH_TOKEN" \
--data-urlencode "client_id=YOUR_CLIENT_ID" \
--data-urlencode "client_secret=YOUR_CLIENT_SECRET"
Guarda el nuevo refresh token que devuelve la respuesta de renovación. Si la renovación falla, reinicia la autorización. Renovar mantiene los scopes concedidos anteriormente. Las credenciales personales no tienen refresh token: solicita un nuevo token de acceso con client_credentials.
GET /public/v1/properties/{property_id}/calendar requiere calendar.read. Indica from y to como fechas inclusivas; una solicitud puede cubrir hasta 730 días.
GET /public/v1/properties/101/calendar?from=2026-12-18&to=2026-12-20
Cada día puede devolver availability, rate, minStay, maxStay, stopSell, closedToArrival, closedToDeparture, restricted y restrictionReasons. availability es el inventario vendible restante tras las reservas confirmadas. Los campos de restricción explican por qué una fecha o una acción de check-in o check-out está restringida.
PATCH /public/v1/properties/{property_id}/calendar requiere calendar.write. La solicitud es parcial: envía solo los controles que deben cambiar y los campos omitidos permanecen sin cambios.
{
"from": "2026-12-18",
"to": "2026-12-31",
"availability": 1,
"rate": 145.0,
"minStay": 2,
"maxStay": 14,
"stopSell": false,
"closedToArrival": false,
"closedToDeparture": false,
"daysOfWeek": [0, 1, 2, 3, 4]
}
daysOfWeek es opcional: 0 es lunes y 6 es domingo. Omítelo para actualizar todas las fechas del rango. La respuesta indica daysUpdated y la actualización normalizada que se aplicó. Los cambios se sincronizan a través de Nowistay PMS.
Las operaciones de calendario requieren titularidad, el derecho de asistente y la gestión de calendario de Nowistay activa. Para habilitar los scopes de calendario en una integración existente, sigue Cómo añadir scopes a un token de API existente, más arriba; renovar un token o rotar su clave secreta no basta.
Los filtros admitidos incluyen propertyId, propertyIds repetido, status, channel, fromDate y toDate. La paginación JSON estándar usa limit (máximo 100) y offset. Los filtros de fechas de las reservas mantienen su semántica de solapamiento: departure >= fromDate y arrival <= toDate.
curl "https://api.nowistay.com/public/v1/bookings?propertyId=101&fromDate=2026-12-01&toDate=2026-12-31&status=confirmed" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
curl "https://api.nowistay.com/public/v1/bookings/9001" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Requiere el scope adicional bookings.sensitive.read. Sin él, se omiten email, phone y el código de la cerradura inteligente.
Las ediciones principales de una reserva requieren bookings.write y una reserva directa elegible. Las reservas de OTA siguen siendo de solo lectura en los campos principales. Un PATCH que contenga únicamente customParameters también admite reservas de OTA y usa booking_parameters.write.
curl -X PATCH "https://api.nowistay.com/public/v1/bookings/9001" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"notes": "Guest requested a baby cot.",
"reservationNumber": "DIRECT-9001",
"silent": true
}'
Las solicitudes PATCH de reservas existentes usan por defecto silent=true. Establece silent=false para activar las notificaciones habituales de actualización. La creación de reservas usa por defecto silent=false. Los campos omitidos permanecen sin cambios y los valores null admitidos siguen vaciando campos. La sincronización de disponibilidad sigue formando parte de la operación compartida.
Los parámetros de reserva personalizados son útiles cuando quieres reflejar en cada reserva de Nowistay datos de tu propio sistema.
curl -X POST "https://api.nowistay.com/public/v1/booking-parameters" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"key": "external_status",
"name": "External status"
}'
curl -X PATCH "https://api.nowistay.com/public/v1/bookings/9001" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"customParameters": {
"external_status": "ready"
}
}'
Ambos ejemplos usan booking_parameters.write. Las actualizaciones mixtas de campos principales y parámetros necesitan ambos scopes de escritura de reservas; si falla la validación, no se modifica ninguna de las dos partes.
Envía null como valor para vaciar un campo personalizado de una reserva.
Requiere missions.write. Los flujos de asignación y notificación también se ejecutan para las misiones creadas por API.
curl -X POST "https://api.nowistay.com/public/v1/missions" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"propertyId": 101,
"bookingId": 9001,
"missionType": "cleaning",
"title": "Checkout cleaning",
"scheduledAt": "2026-12-18T11:30:00Z",
"timezone": "Europe/Paris",
"assignedTeamMemberId": 301
}'
Requiere activity.read.
curl "https://api.nowistay.com/public/v1/properties/101/activity?limit=20" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Los eventos se agrupan en tres categorías: booking, mission y automation (mensajes a huéspedes, eventos de cerraduras inteligentes).
Todas las rutas de esta sección son relativas a /public/v1. Sustituye los IDs de ejemplo por recursos que pertenezcan a la cuenta conectada. El esquema OpenAPI enumera todos los campos, enums y formas de respuesta admitidos.
Usa GET /properties/{id} con properties.read y PATCH /properties/{id} con properties.write. Las listas de alojamientos admiten search. Las ediciones admitidas incluyen nombre, dirección, capacidad, tipo de alojamiento, enlaces de alquiler, portada, horas de check-in y check-out, plazos de cierre de reservas y ajustes predeterminados de precios y estancia.
curl -X PATCH "https://api.nowistay.com/public/v1/properties/101" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"capacity": 4,
"defaultMinStay": 2,
"cutOffDays": 1
}'
POST /bookings requiere bookings.write, el derecho de asistente y una conexión PMS activa. Incluye las fechas, el correo del huésped y sus datos, junto con los campos de precios y operativos admitidos. requestPayment=true requiere además payments.write y crea la reserva pendiente de pago.
curl -X POST "https://api.nowistay.com/public/v1/bookings" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"propertyId": 101,
"arrival": "2026-12-18",
"departure": "2026-12-20",
"guestFirstName": "Maya",
"guestLastName": "Martin",
"guestEmail": "maya@example.com",
"adults": 2,
"amount": "290.00",
"currency": "EUR",
"requestPayment": true
}'
La creación puede tener éxito aunque falle la generación del enlace de pago. La respuesta sigue incluyendo la reserva persistida o su ID, con paymentRequest.sent=false y un error explícito. Comprueba este resultado antes de reintentar para no crear una reserva duplicada. Solo una acción de solicitud de pago autorizada devuelve una URL de checkout.
Lee GET /bookings/{id}/payment-request con bookings.read para obtener el estado seguro de la reserva y de la solicitud de pago. No devuelve URLs de checkout. Esta versión no añade una API independiente de administración de pagos.
El PATCH de reservas existentes admite reservationNumber, cancellationRefundMode (none, full o partial) y cancellationRefundAmount. Un reembolso total o parcial explícito por cancelación requiere bookings.write y payments.write. La cancelación sin reembolso requiere solo bookings.write.
curl -X PATCH "https://api.nowistay.com/public/v1/bookings/9001" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "cancelled",
"cancellationRefundMode": "partial",
"cancellationRefundAmount": "50.00"
}'
Una reserva cancelada no demuestra que el reembolso se haya completado. Comprueba el estado del pago antes de decirle al huésped que su reembolso se ha realizado. payments.write autoriza las solicitudes de pago y los reembolsos por cancelación, no el reajuste de precio de un checkout existente.
POST /guests usa customers.write; la creación duplicada de un huésped devuelve 409. Las lecturas de clientes a nivel de propietario siguen disponibles. Las listas de huéspedes admiten propertyId, propertyIds repetido y los valores de orderBy admitidos: created_at, updated_at, full_name, bookings_count, total_amount, last_stay. Añade el prefijo - para orden descendente; el valor predeterminado es -updated_at.
curl -X POST "https://api.nowistay.com/public/v1/guests" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"firstName": "Maya",
"lastName": "Martin",
"email": "maya@example.com",
"language": "en"
}'
GET /guests/{id}/bookings requiere customers.read y bookings.read y devuelve el historial de estancias elegibles.
GET /missions/{id} y GET /missions/{id}/report usan missions.read. Una misión válida sin informe de finalización devuelve 200 con report: null. Las listas de misiones admiten filtros inclusivos de fechas programadas en UTC fromDate/toDate, propertyIds y teamMemberIds repetidos, además de los filtros existentes.
curl "https://api.nowistay.com/public/v1/missions?propertyIds=101&propertyIds=102&teamMemberIds=301&fromDate=2026-12-01&toDate=2026-12-31" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
GET /conversations admite los filtros propertyId y bookingId. GET /conversations/{id}/messages lee el historial de mensajes. Ambos requieren messages.read. Envía una respuesta con POST /messages y messages.write, indicando exactamente uno de conversationId o bookingId. channelKind o threadChannelId, opcionales, seleccionan un canal; no indiques ambos selectores.
curl -X POST "https://api.nowistay.com/public/v1/messages" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"bookingId": 9001,
"message": "Hello Maya, your arrival instructions are ready."
}'
Los mensajes siguen la vía normal de entrega de las respuestas del anfitrión, incluido el enrutamiento de reservas con varias habitaciones. Un mensaje persistido cuya entrega falla devuelve su ID y el estado de entrega: inspecciona el resultado antes de reintentar. Estos endpoints no admiten archivos adjuntos en los mensajes a huéspedes.
Usa GET /properties/{id}/knowledge-questions y GET /properties/{id}/knowledge-questions/{questionId} con knowledge.read. Usa POST sobre la colección y PATCH sobre una pregunta individual con knowledge.write.
curl -X POST "https://api.nowistay.com/public/v1/properties/101/knowledge-questions" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"question": "Where is the recycling area?",
"answer": "In the courtyard, next to the bicycle shelter."
}'
POST /properties/{id}/knowledge-questions/copy trata {id} como origen y requiere knowledge.read más knowledge.write. Envía targetPropertyIds para fusionar con otros alojamientos. La copia nunca elimina ni sobrescribe las respuestas de destino; todos los destinos se validan antes de empezar a escribir.
curl -X POST "https://api.nowistay.com/public/v1/properties/101/knowledge-questions/copy" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"targetPropertyIds": [102, 103]
}'
GET/PATCH /properties/{id}/welcome-guide: leer o editar la guía, incluidas las introducciones localizadas.GET/POST /properties/{id}/welcome-guide/pages: listar o crear páginas.GET/PATCH /properties/{id}/welcome-guide/pages/{pageId}: leer o editar una página individual y su posición.POST /properties/{id}/welcome-guide/images: subir una imagen desde una URL pública admitida o desde una entrada en base64.Las lecturas requieren guides.read; las escrituras requieren guides.write. Al editar páginas estructuradas, conserva los bloques content/contents de EditorJS y sus IDs, el formato anidado y los medios. Los idiomas omitidos permanecen sin cambios. Se rechaza sustituir una página estructurada por texto plano. No se devuelven los tokens de las guías ni los enlaces de acceso de los huéspedes.
curl -X PATCH "https://api.nowistay.com/public/v1/properties/101/welcome-guide" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"intros": {
"en": "Welcome! Your practical information is below.",
"fr": "Bienvenue ! Retrouvez les informations pratiques ci-dessous."
}
}'
Las descargas de imágenes deben usar direcciones accesibles públicamente. Se comprueban las redirecciones y se aplican límites de tamaño. Consulta el esquema de la solicitud de imagen para ver los campos de URL o base64 admitidos y sus límites.
GET /upsells requiere upsells.read. GET /bookings/{id}/upsell-purchases requiere upsells.read y bookings.read. Estos endpoints son de solo lectura.
curl "https://api.nowistay.com/public/v1/bookings/9001/upsell-purchases" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Añade responseFormat=table a las listas de reservas o de misiones para obtener columns, rows y pagination, con limit de hasta 500. El JSON estándar sigue siendo el valor predeterminado y mantiene su máximo de 100. Usa offset para obtener las páginas siguientes. Si se indican propertyId y varios propertyIds, se intersectan.
curl "https://api.nowistay.com/public/v1/bookings?propertyIds=101&propertyIds=102&fromDate=2026-12-01&toDate=2026-12-31&responseFormat=table&limit=500&offset=0" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Las tablas de reservas usan bookings.read; las de misiones usan missions.read. Las asignaciones de limpieza de los informes de reservas requieren además missions.read, y la presencia en conversaciones requiere messages.read. Los filtros de fechas de las reservas conservan la semántica de solapamiento; los filtros de fechas de las misiones usan fechas programadas en UTC.
Las rutas REST existentes, los valores JSON estándar por defecto, la paginación, la redacción de las reservas, el vaciado con null admitido y los valores predeterminados de notificación siguen disponibles. Las restricciones de reservas relacionadas con pagos descritas más arriba se aplican ahora por primera vez. REST y MCP comparten las operaciones subyacentes y sus efectos secundarios, mientras que los contratos de las herramientas MCP y el comportamiento de vista previa y confirmación no cambian.
Una página de Nowistay contiene un documento EditorJS por idioma. El array blocks define el orden de visualización. Cada objeto de ese array es un bloque; coloca los ejemplos siguientes dentro de content.blocks o contents.<locale>.blocks. Los valores de ejemplo son ficticios y deben adaptarse a tu alojamiento.
Estructura común: id es una cadena estable, única dentro del documento; conserva los IDs existentes y asigna uno nuevo a cada bloque nuevo. type es el nombre exacto del bloque, sensible a mayúsculas y minúsculas. data es un objeto cuyos campos dependen del tipo. Conserva también los tunes y los metadatos del documento existentes que devuelve una lectura, en lugar de reconstruirlos sin necesidad.
Solicitud de creación: POST /public/v1/properties/{id}/welcome-guide/pages con guides.write. Usa locale + title + content para un idioma, o titles + contents para varios. icon es info por defecto. position admite start, end o after_page; este último requiere afterPageId. Aquí tienes un cuerpo de solicitud completo en inglés y francés:
{
"titles": {
"en": "Arrival",
"fr": "Arrivée"
},
"contents": {
"en": {
"blocks": [
{
"id": "arrival001",
"type": "header",
"data": {
"text": "Getting here",
"level": 2
}
},
{
"id": "arrival002",
"type": "paragraph",
"data": {
"text": "Use the <b>main entrance</b>."
}
}
]
},
"fr": {
"blocks": [
{
"id": "arrival001",
"type": "header",
"data": {
"text": "Venir au logement",
"level": 2
}
},
{
"id": "arrival002",
"type": "paragraph",
"data": {
"text": "Utilisez l’<b>entrée principale</b>."
}
}
]
}
},
"position": "end"
}
data.text es el texto del encabezado. Usa data.level: 2, igual que el editor de guías. El título de la propia página va en title o titles, fuera de los bloques.
{
"id": "guide00001",
"type": "header",
"data": {
"text": "Arrival / Arrivée",
"level": 2
}
}
data.text es una cadena. Usa el marcado en línea <b>, <i> y <a href="https://…"> para negrita, cursiva y enlaces. Escapa las comillas dobles dentro de las cadenas JSON. No envíes una página HTML completa ni Markdown como content.
{
"id": "guide00002",
"type": "paragraph",
"data": {
"text": "<b>Wi-Fi</b>: GuestNetwork. <i>Welcome / Bienvenue !</i> <a href=\"https://example.com/\">Information</a>"
}
}
Usa style: unordered. items contiene objetos, no cadenas. Cada elemento tiene content (texto con formato), meta (un objeto) e items (un array de elementos hijos, vacío en las hojas). Repite la misma estructura para el anidamiento.
{
"id": "guide00003",
"type": "list",
"data": {
"style": "unordered",
"meta": {},
"items": [
{
"content": "Kitchen / Cuisine",
"meta": {},
"items": [
{
"content": "Coffee machine / Machine à café",
"meta": {},
"items": []
}
]
}
]
}
}
Usa style: ordered con meta.counterType: numeric en la raíz. Mantén la misma estructura de elementos y arrays anidados que en una lista con viñetas.
{
"id": "guide00004",
"type": "list",
"data": {
"style": "ordered",
"meta": {
"counterType": "numeric"
},
"items": [
{
"content": "Close the windows / Fermez les fenêtres",
"meta": {},
"items": []
},
{
"content": "Return the keys / Rendez les clés",
"meta": {},
"items": []
}
]
}
}
Usa style: checklist. Pon el estado booleano inicial de la casilla en meta.checked de cada elemento. Es contenido de la guía, no un informe de finalización de una misión.
{
"id": "guide00005",
"type": "list",
"data": {
"style": "checklist",
"meta": {},
"items": [
{
"content": "Windows closed / Fenêtres fermées",
"meta": {
"checked": false
},
"items": []
}
]
}
}
data.text es la cita, caption es su atribución (o una cadena vacía) y alignment es el ajuste de alineación del editor. Usa left; la vista del huésped aplica su propio diseño de cita.
{
"id": "guide00006",
"type": "quote",
"data": {
"text": "Please respect the neighbours / Merci de respecter le voisinage.",
"caption": "Your host / Votre hôte",
"alignment": "left"
}
}
Un separador visual tiene un objeto data vacío. Su posición en blocks determina dónde aparece la separación.
{
"id": "guide00007",
"type": "delimiter",
"data": {}
}
data.content es un array de filas; cada fila es un array de cadenas de celdas. Mantén el mismo número de celdas en todas las filas. Con withHeadings: true, la primera fila aporta los encabezados de columna. Usa false cuando todas las filas contienen datos normales.
{
"id": "guide00008",
"type": "table",
"data": {
"withHeadings": true,
"content": [
[
"Service",
"Time / Horaire"
],
[
"Check-in / Arrivée",
"16:00"
],
[
"Check-out / Départ",
"10:00"
]
]
}
}
data.file.url es la URL de la imagen subida, no un nombre de archivo local ni una cadena en base64. caption es una cadena; withBorder, withBackground y stretched son booleanos. Primero envía la imagen por POST a /public/v1/properties/{id}/welcome-guide/images y después inserta en content.blocks el editorJsImageBlock devuelto. Conserva los campos devueltos y usa un ID de bloque único. La URL de abajo es ilustrativa: sustitúyela por la URL real devuelta.
{
"id": "guide00009",
"type": "image",
"data": {
"file": {
"url": "https://images.example.com/replace-with-uploaded-image.jpg"
},
"caption": "Entrance / Entrée",
"withBorder": false,
"withBackground": false,
"stretched": false
}
}
Usa service: youtube para un vídeo normal de YouTube, o youtubeShort para un Short. source es su URL original; embed es la URL de inserción de YouTube correspondiente. width y height son números y caption es una cadena. El editor usa 580 × 320 para un vídeo normal y 315 × 560 para un Short. Sustituye VIDEO_ID_11 en ambas URLs por el mismo ID real de 11 caracteres del vídeo; en los Shorts, la ruta de origen es /shorts/VIDEO_ID_11. El HTML arbitrario de iframe no es el formato del bloque.
{
"id": "guide00010",
"type": "video",
"data": {
"service": "youtube",
"source": "https://www.youtube.com/watch?v=VIDEO_ID_11",
"embed": "https://www.youtube.com/embed/VIDEO_ID_11",
"width": 580,
"height": 320,
"caption": "Arrival instructions / Instructions d’arrivée"
}
}
embed usa los mismos campos data que video. Conserva el tipo existente al editar una página; ambos muestran YouTube y YouTube Shorts. No des por hecho que Nowistay admite todos los proveedores de inserción compatibles con EditorJS.
{
"id": "guide00011",
"type": "embed",
"data": {
"service": "youtubeShort",
"source": "https://www.youtube.com/shorts/VIDEO_ID_11",
"embed": "https://www.youtube.com/embed/VIDEO_ID_11",
"width": 315,
"height": 560,
"caption": ""
}
}
Usa un objeto data vacío para colocar aquí el catálogo de upsells elegibles. Los productos, los precios y la disponibilidad proceden del catálogo configurado y del contexto del huésped; no se incrustan en este bloque.
{
"id": "guide00012",
"type": "upsellCatalog",
"data": {}
}
data.itemId es el ID numérico de un upsell existente. Encuentra el ID correcto con GET /public/v1/upsells usando upsells.read; sustituye el 401 ficticio de abajo. Conserva el ID original salvo que quieras seleccionar otro producto a propósito. Escribir la página usa guides.write; este bloque no crea un producto, no fija un precio ni lo compra.
{
"id": "guide00013",
"type": "upsellProduct",
"data": {
"itemId": 401
}
}
Validación REST: la creación y las actualizaciones de páginas rechazan con HTTP 422 los tipos de bloque no admitidos y los datos mal formados. Se comprueba cada idioma indicado antes de escribir nada, incluidos los elementos de listas anidadas, las celdas de tablas y los campos multimedia. El error identifica el documento y el bloque implicados. Los IDs siguen siendo opcionales en los documentos existentes; cuando se indican, deben ser cadenas únicas y no vacías. Se conservan los metadatos y el formato. Esta validación estructural no comprueba si una imagen es accesible ni si un upsell existe.
imageLibrary es el control selector de imágenes del editor, no un bloque de galería de la API. Usa bloques image. linkPreview es un ajuste de bloque (tunes), no un tipo de contenido independiente; conserva los datos existentes. No inventes bloques html, code o button ni proveedores de vídeo no documentados: que se acepte un objeto JSON no garantiza que se muestre en la guía.
Lee primero GET /public/v1/properties/{id}/welcome-guide/pages/{pageId} con guides.read para obtener content y contents. Trabaja sobre una copia del documento completo del idioma que estás editando, cambia solo los bloques deseados y envía después ese documento por PATCH a la misma URL con guides.write. Si envías un solo bloque, el documento se sustituye por ese único bloque; los bloques no se fusionan por id. Los idiomas omitidos permanecen sin cambios. Para mover una página existente, usa order, no position.
Los campos text y texts no sustituyen al documento estructurado. Sigue las indicaciones simpleTextUpdateAllowed, recommendedUpdateMode y recommendedUpdateField de la respuesta de lectura; una página estructurada rechaza la sustitución por texto plano. Los ejemplos individuales de esta sección describen bloques, no solicitudes PATCH completas para una página existente. Vuelve a leer la página tras editarla y comprueba idiomas, IDs, enlaces y medios. Con solo guides.write, la respuesta de escritura es mínima; añade guides.read para recibir los detalles.
Si no quieres consultar la API de forma repetida para detectar cambios, puedes suscribirte a webhooks. Nowistay llamará entonces a una URL que indiques cada vez que se cree o actualice una reserva en el alojamiento, y enviará los mismos datos de reserva que devuelve GET /public/v1/bookings/{id}.
Hoy se admiten dos eventos:
booking.created: se ha registrado una nueva reserva en el alojamiento.booking.updated: una reserva existente ha cambiado (fechas, estado, datos del huésped, código de la cerradura inteligente, etc.).Cada webhook está vinculado a un único alojamiento y puedes registrar hasta tres por alojamiento. Las entregas van firmadas (HMAC-SHA256) para que puedas verificar que realmente proceden de Nowistay, se reintentan con espera exponencial si fallan y el endpoint se desactiva automáticamente si sigue fallando durante más de 72 horas. Los payloads de los webhooks no contienen campos sensibles del huésped (correo, teléfono, dirección, código de la cerradura inteligente). Cuando los necesites, llama a la API con el scope bookings.sensitive.read.
Los detalles completos de configuración y de verificación de la firma están en el artículo dedicado: API pública: recibe webhooks de reservas en tiempo real.
Cuando alcances el límite, la API devuelve 429 Rate limit exceeded. Espera y vuelve a intentarlo tras unos segundos.
Todos los errores siguen la misma forma, con un code estable, un mensaje legible y un requestId que puedes incluir en tus solicitudes a soporte.
{
"error": {
"code": "validation_error",
"message": "The request contains an invalid value.",
"requestId": "example-request-id"
}
}
400: solicitud incorrecta (cuerpo mal formado, campo ausente).
silent en las escrituras. Ponlo en true cuando sincronices datos en bloque y no quieras activar notificaciones a los huéspedes.requestId en tus registros. Acelera mucho la resolución de las solicitudes a soporte./public/openapi.json.Guías para anfitriones: lee el blog.
¿Listo para poner tu alojamiento en piloto automático?