Portal del huésped
El enlace del huésped, además del registro, es la guía de su estancia: cómo llegar, cómo entrar, cómo funciona la casa, qué hay alrededor y a quién llamar. Los códigos de acceso, solo con el registro completo.
Hasta ahora el enlace de cada reserva servía para una cosa: que el huésped se registrara y firmara. Con el portal encendido, ese mismo enlace es también la guía de la estancia, con tu marca y en el idioma del huésped. No hay un segundo enlace ni una app: lo que ya reparte tu PMS, o lo que mandan nuestros correos, lleva al portal.
Viene apagado, en todas las cuentas. Con el portal apagado el enlace funciona exactamente como hasta ahora: ni se consulta el contenido. Puedes rellenar y previsualizar («Ver como huésped», en cada propiedad) antes de encenderlo, y va incluido en el precio: no cambia lo que pagas por reserva comunicada.
Qué ve el huésped
El portal se reparte en cuatro secciones, con una barra de iconos abajo en el móvil y un menú lateral en escritorio. Solo salen las que tienen contenido, y la sección va en el enlace (#casa), así que un mensaje puede llevar directo a una de ellas.
| Campo | Tipo | Descripción |
|---|---|---|
Estancia | siempre | Lo de siempre: fechas y horas, el registro, la firma y los huéspedes del grupo. |
La casa | siempre · acceso con candado | En el orden en que lo recorre el huésped: cómo llegar (con botón «Dirección de la casa» que abre su app de mapas), dónde aparcar (con su mapa), cómo entrar —instrucciones, códigos y wifi, detrás del candado—, cómo funciona la casa por temas, las normas que firmó en el contrato y qué hacer al salir. |
La zona | siempre | Tus recomendaciones por categoría —restaurantes, cafés, supermercado, farmacia, playa, visitas, transporte, ocio—, cada una con su descripción por idioma y un botón al mapa. |
Ayuda | siempre | Tu teléfono (el de la propiedad o el de la cuenta), WhatsApp, el 112 y los teléfonos útiles que pongas: taxi, centro de salud, farmacia de guardia, policía local, mantenimiento. |
Cada texto va por idioma: el huésped lee el suyo, si falta, el castellano, y si tampoco, el que haya. Las categorías, los temas del manual y los tipos de teléfono se traducen solos; los nombres propios (un restaurante, un taxi) no.
Cuándo se abren los códigos
Las instrucciones de acceso, los códigos y el wifi van detrás de un candado que se abre cuando se cumplen dos condiciones a la vez:
| Campo | Tipo | Descripción |
|---|---|---|
Registro completo | condición | Todos los huéspedes registrados y el contrato firmado. Es exactamente la misma condición que dispara el webhook checkin.completed, así que el portal y tu PMS nunca se contradicen. |
Dentro de la ventana | condición | Desde N horas antes de la hora de entrada (48 por defecto, de 1 a 168, se ajusta por cuenta) hasta el final del día de salida. Menos de una hora no deja margen a quien llega antes; más de una semana deja los códigos a la vista de un enlace reenviado demasiado tiempo. |
Los códigos no viajan con la página: el huésped pulsa «Mostrar los códigos» y se piden en ese momento. Cada vez queda apuntada —cuándo, desde qué red (recortada: sin el último octeto) y por qué se pudo— durante 90 días, y se consulta por API. Lo que no se guarda nunca en ese registro son los códigos en sí.
Desbloquear ya. El huésped está en la puerta y no ha firmado: un botón en la ficha de la reserva —y una llamada a la API— abre el acceso sin esperar al registro ni a la ventana. Queda registrado quién lo abrió y cuándo, se cierra igual al acabar el día de salida, y se puede deshacer.
| Campo | Tipo | Descripción |
|---|---|---|
pending | cerrado | Falta alguien por registrarse o por firmar. |
tooEarly | cerrado | Registro completo, pero todavía es pronto: se abre en opensAt. |
open | abierto | Se ven los códigos, hasta closesAt. openedBy dice si fue por el registro o por un desbloqueo manual. |
ended | cerrado | Ya pasó el día de salida. |
Si lo activas, cuando el acceso se abre el huésped recibe un correo «todo listo para tu llegada» con el enlace al portal. Nunca con los códigos: el correo se reenvía; el portal, con su registro de quién lo miró, no.
Encenderlo y ajustarlo
/settings/settingsEn el panel, Portal del huésped → Ajustes. Por API, el objeto guestPortal de /settings. Como el resto de los ajustes, solo cambia lo que mandes.
| Campo | Tipo | Descripción |
|---|---|---|
guestPortal.enabled | boolean | Encender o apagar el portal en la cuenta. Apagado, el enlace del huésped funciona como siempre. |
guestPortal.hoursBefore | integer 1-168 | Desde cuántas horas antes de la entrada se pueden ver los códigos y el wifi. Por defecto, 48. |
guestPortal.phone | string | null | Teléfono de contacto de la cuenta, el que ve el huésped en Ayuda. Cada propiedad puede tener el suyo. null lo quita. |
guestPortal.readyEmail | boolean | Correo «todo listo para tu llegada» con el enlace (nunca los códigos) cuando el acceso se hace visible. |
curl -X PATCH https://api.kinapi.es/api/v1/settings \
-H "ocp-apim-subscription-key: TU_API_KEY" \
-H "content-type: application/json" \
-d '{"guestPortal":{"enabled":true,"hoursBefore":24,"phone":"+34600000000"}}'Lo común, los bloques y lo propio
Cincuenta propiedades no se rellenan una a una. Siete secciones del portal se pueden compartir —la llegada, el aparcamiento, el manual de la casa, las normas, la zona, la salida y los teléfonos útiles— y se escriben en tres sitios:
| Campo | Tipo | Descripción |
|---|---|---|
Lo común | cuenta | Se escribe una vez y vale para todas las propiedades que no digan otra cosa. Las normas de la casa, la salida o los teléfonos útiles suelen ser iguales en todo el parque. |
Un bloque | variante con nombre | Una versión de una sección con nombre propio —«Parking Alameda», «Zona centro»— que se asigna a las propiedades que la usan. Un edificio con seis pisos tiene un solo aparcamiento: se escribe una vez y se asigna seis veces. |
Lo propio | propiedad | Lo de ese piso. Y lo que solo puede ser propio, porque no se comparte: la dirección (el mapa de la casa), el acceso, los códigos, el wifi y el teléfono. |
Para cada sección de cada propiedad se decide de dónde sale, y la regla es siempre la misma:
| Campo | Tipo | Descripción |
|---|---|---|
hidden | 1.º | La propiedad esconde la sección: no se enseña nada, aunque lo común la tenga. |
block | 2.º | La propiedad usa un bloque (blockId): se enseña el bloque, si tiene contenido. |
own | 3.º | La propiedad tiene contenido propio para la sección: se enseña el suyo. |
common | 4.º | Nada de lo anterior: se enseña lo común de la cuenta. |
empty | 5.º | No hay nada en ningún sitio: la sección no sale. |
El manual de la casa es la excepción: se compone tema a tema. La propiedad escribe la calefacción y la basura, lo común tiene la cocina y la lavadora, y el huésped ve los cuatro temas; el que esté en los dos sitios sale con el texto de la propiedad.
En el panel, el apartado Portal del huésped del menú tiene tres pestañas: Propiedades (una tabla con el estado de cada sección en cada propiedad —✓ propio, ≡ común, ⧉ bloque— y la asignación en bloque de una variante a varias propiedades), Común y Ajustes. En cada propiedad, cada sección compartible lleva su selector —común, una variante, propio o escondida—, y la ficha tiene «Copiar de otra propiedad» (todo menos códigos y wifi) y «Anterior / Siguiente» para recorrer el parque sin volver a la lista.
Lo común de la cuenta
/portal/common/portal/commonEl PUT sustituye todo lo común: la sección que no mandes se borra. Lee, cambia y escribe. En la lectura solo vienen las secciones con contenido.
| Campo | Tipo | Descripción |
|---|---|---|
arrival | texto por idioma | Cómo llegar: entrada, planta, puerta, qué buscar. Un objeto con un texto por idioma (es, en, de, fr, nl, it, pt); los saltos de línea se conservan y los enlaces https se vuelven enlaces. |
parking | texto por idioma | Dónde dejar el coche. |
parkingMapUrl | string | Enlace https a cualquier servicio de mapas (Google Maps, Apple Maps, Waze…). El huésped ve un botón que lo abre en su app. |
manual | objeto | Cómo funciona la casa, por temas fijos: climatizacion, cocina, aguaCaliente, lavadora, basura, tv, exteriores, otros. Cada tema, un texto por idioma. Los títulos de los temas se traducen solos; el tema que no mandes no sale. |
showHouseRules | boolean | Enseñar en el portal las normas de la casa que el huésped firma en el contrato (las de la reserva, si no las de la propiedad, si no las predeterminadas de la cuenta). No se escriben aquí: ya existen. |
area | objeto | { enabled, places }. enabled: false esconde la sección sin borrar los sitios. Cada sitio: category (restaurante, cafe, supermercado, farmacia, playa, visita, transporte, ocio, otro), name, mapUrl y description por idioma. |
departure | texto por idioma | Qué hacer antes de irse y dónde dejar las llaves. |
usefulNumbers | array | Teléfonos útiles, debajo del tuyo y del 112: type (taxi, centroSalud, farmacia, policia, mantenimiento, otro), name opcional («Radio Taxi Tarifa») y number. |
curl -X PUT https://api.kinapi.es/api/v1/portal/common \
-H "ocp-apim-subscription-key: TU_API_KEY" \
-H "content-type: application/json" \
-d '{
"departure": { "es": "Deja las llaves en la caja y cierra la puerta al salir.", "en": "Leave the keys in the box and shut the door behind you." },
"showHouseRules": true,
"usefulNumbers": [
{ "type": "taxi", "name": "Radio Taxi", "number": "+34 956 000 000" },
{ "type": "centroSalud", "number": "+34 956 111 111" }
]
}'Bloques
/portal/blocks/portal/blocks/portal/blocks/{id}/portal/blocks/{id}/portal/blocks/{id}| Campo | Tipo | Descripción |
|---|---|---|
sectionrequerido | string | De qué sección es variante: arrival, parking, manual, houseRules, area, departure o usefulNumbers. No se puede cambiar después. Solo en el POST. |
namerequerido | string | Tu nombre para el bloque («Parking Alameda»). El huésped no lo ve nunca. Hasta 60 caracteres. |
content | objeto | Los mismos campos que lo común; solo se conservan los de la sección del bloque. En el PATCH sustituye el contenido. |
propertyIds | string[] | Las propiedades que usan este bloque en vez de lo común o de lo suyo. En el PATCH es la lista completa: la que falte deja de usarlo y vuelve a lo suyo o a lo común. |
curl -X POST https://api.kinapi.es/api/v1/portal/blocks \
-H "ocp-apim-subscription-key: TU_API_KEY" \
-H "content-type: application/json" \
-d '{
"section": "parking",
"name": "Parking Alameda",
"content": {
"parking": { "es": "Plaza 14 del parking de la Alameda, a 200 m. El mando abre la barrera.", "en": "Bay 14 at the Alameda car park, 200 m away. The remote opens the barrier." },
"parkingMapUrl": "https://maps.app.goo.gl/…"
},
"propertyIds": ["b8f3a2c1-…", "5c7d9e01-…"]
}'{
"id": "3f9c…",
"section": "parking",
"name": "Parking Alameda",
"content": { "parking": { "es": "…", "en": "…" }, "parkingMapUrl": "https://maps.app.goo.gl/…" },
"propertyIds": ["b8f3a2c1-…", "5c7d9e01-…"],
"updatedAt": "2026-10-23T10:00:00.000Z"
}El id es lo que va en los origins de una propiedad como block:3f9c…. Borrar un bloque devuelve sus propiedades a lo suyo o a lo común; no borra nada de ellas.
El portal de una propiedad
/properties/{propertyId}/portal/properties/{propertyId}/portalLo propio de cada alojamiento: las secciones compartibles de arriba, más lo que solo puede ser suyo y de dónde sale cada sección. El PUT sustituye el portal entero, códigos y wifi incluidos: lo que no mandes se borra.
| Campo | Tipo | Descripción |
|---|---|---|
houseMapUrl | string | Enlace de mapa a la casa. El huésped ve un botón «Dirección de la casa». |
access | texto por idioma | Cómo entrar: dónde está la caja de llaves, qué abre cada código. Solo se ve con el registro completo y dentro de la ventana. |
codes | array | Los códigos, cifrados en reposo: type (portal, puerta, cajaLlaves, garaje, otro) y value tal cual se teclea. Detrás del botón «Mostrar los códigos», y cada vez que se enseñan queda apuntado. |
wifi | objeto | { network, password }. Sin password, red abierta. Se enseña con los códigos. |
phone | string | Teléfono de contacto de esta propiedad. Sin él, el de la cuenta (guestPortal.phone). |
origins | objeto | Por sección compartible, de dónde sale en esta propiedad: common, own, hidden o block:<id>. Lo que no mandes: own si la propiedad trae contenido para esa sección, common si no. Con common o un bloque, el contenido propio que mandes para esa sección se descarta. |
curl -X PUT https://api.kinapi.es/api/v1/properties/b8f3a2c1-…/portal \
-H "ocp-apim-subscription-key: TU_API_KEY" \
-H "content-type: application/json" \
-d '{
"houseMapUrl": "https://maps.app.goo.gl/…",
"arrival": { "es": "Portal verde junto a la farmacia. 2.º izquierda.", "en": "Green street door next to the pharmacy. 2nd floor, left." },
"access": { "es": "La caja de llaves está a la derecha del portal; el código del portal abre también el garaje." },
"codes": [ { "type": "portal", "value": "4821" }, { "type": "cajaLlaves", "value": "0719" } ],
"wifi": { "network": "Sol3B", "password": "tarifa-2026" },
"manual": { "climatizacion": { "es": "El termostato está en el pasillo; 21 ºC es suficiente." } },
"origins": { "parking": "block:3f9c…", "area": "hidden" }
}'{
"propertyId": "b8f3a2c1-…",
"houseMapUrl": "https://maps.app.goo.gl/…",
"arrival": { "es": "…", "en": "…" },
"access": { "es": "…" },
"codes": [ { "type": "portal", "value": "4821" }, { "type": "cajaLlaves", "value": "0719" } ],
"wifi": { "network": "Sol3B", "password": "tarifa-2026" },
"manual": { "climatizacion": { "es": "…" } },
"origins": {
"arrival": { "source": "own" },
"parking": { "source": "block", "blockId": "3f9c…" },
"manual": { "source": "own" },
"houseRules": { "source": "common" },
"area": { "source": "hidden" },
"departure": { "source": "common" },
"usefulNumbers": { "source": "common" }
},
"updatedAt": "2026-10-23T10:05:00.000Z"
}Los códigos vienen en claro aquí, y solo aquí: abren la casa. Nunca salen en los listados, la respuesta lleva cache-control: no-store, y conviene que tu integración los trate igual que una contraseña. Los campos de arriba son el contenido propio; origins dice lo que ve el huésped después de resolver bloques y lo común, y manual se compone tema a tema con el común.
El acceso de una reserva
/checkins/{reservationId}/portal/checkins/{reservationId}/portal/unlock/checkins/{reservationId}/portal/unlockSi el huésped de una reserva ya puede ver los códigos, desde o hasta cuándo, si alguien lo desbloqueó a mano y cuándo los ha visto. El POST es «Desbloquear ya» —queda firmado api: más la etiqueta de tu clave— y el DELETE lo deshace. Los tres devuelven el mismo objeto; nunca los códigos, que son de la propiedad.
{
"enabled": true,
"state": "open",
"closesAt": "2026-08-18T21:59:59.999Z",
"openedBy": "unlock",
"unlocked": { "at": "2026-08-14T13:02:11.000Z", "by": "api:PMS" },
"reveals": [
{ "at": "2026-08-14T15:40:02.000Z", "network": "83.45.12.0", "reason": "unlock" }
]
}Una reserva cancelada no se desbloquea: 409 cancelled. No hay eventos nuevos de webhook: checkin.completed sigue siendo el momento en que el acceso se abre, si la reserva ya está en ventana.
Las horas de entrada y salida
La ventana de los códigos y lo que lee el huésped en Estancia dependen de la hora de entrada y de salida de la reserva, y esa hora se resuelve en cascada: la reserva → la propiedad → la cuenta → 16:00 / 11:00. Una reserva que trae su hora la conserva; la que no, hereda la de la propiedad (checkInTime y checkOutTime en /properties), y si la propiedad no tiene, la de la cuenta (stay en /settings).
Cambiar la hora de una propiedad o de la cuenta mueve las reservas futuras que la heredaban y aún no se han comunicado, igual que desde la ficha. Las que trajeron la suya no se tocan.
Desde un agente
El servidor MCP tiene una herramienta por cada operación de arriba, para configurar el portal hablando:
| Campo | Tipo | Descripción |
|---|---|---|
kinapi_get_property_portal · kinapi_set_property_portal | propiedad | El portal de una propiedad, con de dónde sale cada sección; y sustituirlo. |
kinapi_get_portal_common · kinapi_set_portal_common | cuenta | Lo común, y sustituirlo. |
kinapi_list_portal_blocks · kinapi_create_portal_block · kinapi_update_portal_block · kinapi_delete_portal_block | bloques | Las variantes con nombre y a qué propiedades se aplican. |
kinapi_get_checkin_access · kinapi_unlock_checkin_access · kinapi_lock_checkin_access | reserva | Si el huésped ya puede ver los códigos, «Desbloquear ya» y deshacerlo. |
kinapi_update_settings | cuenta | Encender el portal, la ventana, el teléfono, el correo de «todo listo» y las horas por defecto. |
«Pon en lo común que las llaves se dejan en la caja y que se enseñen las normas de la casa» «Crea un bloque de aparcamiento “Parking Alameda” con este texto y asígnalo a los seis pisos de la calle Alameda» «¿Ha visto ya los códigos el huésped de la reserva 4821?» «El huésped de la 4821 está en la puerta y no ha firmado: ábrele el acceso»