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.

Las cuatro secciones
CampoTipoDescripción
EstanciasiempreLo de siempre: fechas y horas, el registro, la firma y los huéspedes del grupo.
La casasiempre · acceso con candadoEn 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 zonasiempreTus 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.
AyudasiempreTu 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:

CampoTipoDescripción
Registro completocondiciónTodos 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 ventanacondiciónDesde 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.

Estado del acceso de una reserva
CampoTipoDescripción
pendingcerradoFalta alguien por registrarse o por firmar.
tooEarlycerradoRegistro completo, pero todavía es pronto: se abre en opensAt.
openabiertoSe ven los códigos, hasta closesAt. openedBy dice si fue por el registro o por un desbloqueo manual.
endedcerradoYa 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

GET/settings
PATCH/settings

En 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.

guestPortal
CampoTipoDescripción
guestPortal.enabledbooleanEncender o apagar el portal en la cuenta. Apagado, el enlace del huésped funciona como siempre.
guestPortal.hoursBeforeinteger 1-168Desde cuántas horas antes de la entrada se pueden ver los códigos y el wifi. Por defecto, 48.
guestPortal.phonestring | nullTeléfono de contacto de la cuenta, el que ve el huésped en Ayuda. Cada propiedad puede tener el suyo. null lo quita.
guestPortal.readyEmailbooleanCorreo «todo listo para tu llegada» con el enlace (nunca los códigos) cuando el acceso se hace visible.
curl
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:

CampoTipoDescripción
Lo comúncuentaSe 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 bloquevariante con nombreUna 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 propiopropiedadLo 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:

De dónde sale cada sección (origins)
CampoTipoDescripción
hidden1.ºLa propiedad esconde la sección: no se enseña nada, aunque lo común la tenga.
block2.ºLa propiedad usa un bloque (blockId): se enseña el bloque, si tiene contenido.
own3.ºLa propiedad tiene contenido propio para la sección: se enseña el suyo.
common4.ºNada de lo anterior: se enseña lo común de la cuenta.
empty5.º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

GET/portal/common
PUT/portal/common

El 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.

Las secciones compartibles (lo común, un bloque y la propiedad admiten las mismas)
CampoTipoDescripción
arrivaltexto por idiomaCó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.
parkingtexto por idiomaDónde dejar el coche.
parkingMapUrlstringEnlace https a cualquier servicio de mapas (Google Maps, Apple Maps, Waze…). El huésped ve un botón que lo abre en su app.
manualobjetoCó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.
showHouseRulesbooleanEnseñ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.
areaobjeto{ 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.
departuretexto por idiomaQué hacer antes de irse y dónde dejar las llaves.
usefulNumbersarrayTeléfonos útiles, debajo del tuyo y del 112: type (taxi, centroSalud, farmacia, policia, mantenimiento, otro), name opcional («Radio Taxi Tarifa») y number.
curl
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

GET/portal/blocks
POST/portal/blocks
GET/portal/blocks/{id}
PATCH/portal/blocks/{id}
DELETE/portal/blocks/{id}
Body
CampoTipoDescripción
sectionrequeridostringDe qué sección es variante: arrival, parking, manual, houseRules, area, departure o usefulNumbers. No se puede cambiar después. Solo en el POST.
namerequeridostringTu nombre para el bloque («Parking Alameda»). El huésped no lo ve nunca. Hasta 60 caracteres.
contentobjetoLos mismos campos que lo común; solo se conservan los de la sección del bloque. En el PATCH sustituye el contenido.
propertyIdsstring[]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
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-…"]
  }'
201 Created
{
  "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

GET/properties/{propertyId}/portal
PUT/properties/{propertyId}/portal

Lo 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.

Body (además de las secciones compartibles)
CampoTipoDescripción
houseMapUrlstringEnlace de mapa a la casa. El huésped ve un botón «Dirección de la casa».
accesstexto por idiomaCó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.
codesarrayLos 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.
wifiobjeto{ network, password }. Sin password, red abierta. Se enseña con los códigos.
phonestringTeléfono de contacto de esta propiedad. Sin él, el de la cuenta (guestPortal.phone).
originsobjetoPor 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
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" }
  }'
200 OK
{
  "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

GET/checkins/{reservationId}/portal
POST/checkins/{reservationId}/portal/unlock
DELETE/checkins/{reservationId}/portal/unlock

Si 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.

200 OK
{
  "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:

CampoTipoDescripción
kinapi_get_property_portal · kinapi_set_property_portalpropiedadEl portal de una propiedad, con de dónde sale cada sección; y sustituirlo.
kinapi_get_portal_common · kinapi_set_portal_commoncuentaLo común, y sustituirlo.
kinapi_list_portal_blocks · kinapi_create_portal_block · kinapi_update_portal_block · kinapi_delete_portal_blockbloquesLas variantes con nombre y a qué propiedades se aplican.
kinapi_get_checkin_access · kinapi_unlock_checkin_access · kinapi_lock_checkin_accessreservaSi el huésped ya puede ver los códigos, «Desbloquear ya» y deshacerlo.
kinapi_update_settingscuentaEncender el portal, la ventana, el teléfono, el correo de «todo listo» y las horas por defecto.
Ejemplos de lo que se le puede pedir
«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»