Appearance
PMS and hotel systems
Room-and-surname sign-in, and the connection behind it.
How room login works
PMS integration is a connector, configured under Connectors › PMS like any other connector type. What makes it different is pass_mode, which decides what a guest has to type as their password once their room has been checked in: a fixed pre-defined password, the room number itself (username and password are the same value), or a fuzzy match against the guest's name as the PMS reported it — first name, last name, or full name, scored for similarity against a configurable threshold (pass_percentage, default 80%) rather than compared exactly, since PMS systems are inconsistent about spacing, punctuation and case in guest names. There is also a stored/encrypted-password mode, and a use_first_login_only switch that, once a fuzzy-mode guest has logged in successfully, locks their account to whatever password worked the first time rather than re-matching the name on every subsequent login. All of this is evaluated in one place, as part of the login check, on every login attempt where the journey's integration is set to PMS.
Supported PMS
Kiwire integrates with hotel PMS platforms in two different ways depending on how the vendor exposes data:
- HTTP-inbound: the PMS (or middleware in front of it) pushes events to Kiwire itself, at
/pms/inbound/{tenantId}for JSON/GET/IDB/Pro/eZee/Rhealta/Comanche-style payloads, or to/api/v1/pms/eventand/api/v1/pms/syncwhen the event is relayed through the Kiwire Agent app (which also polls/api/v1/pms/pending-action). Each PMS type has its own handler that knows how to parse that vendor's payload shape into a common room/action event. - Socket-based: for Opera, Infor and Oasis (Winpac, Hospital and PMSI are also listed as socket types, but see the daemon section below — nothing drives them yet), Kiwire instead holds an open, persistent connection out to the property's PMS server. Enabling one of these connector types provisions a dedicated Supervisor-managed process per connector (
kiwire-pms-{tenantId}-{connectorId}), automatically started, restarted or stopped as the connector is enabled, disabled, or its type changed.
The PMS log
Admin › Monitoring › PMS Log gives a live view of everything the PMS integration is doing, without needing shell access. It uses the same log-viewer pattern as the portal log elsewhere in Kiwire: files are listed grouped by local date (in the tenant's own timezone), a file can be read with an optional case-insensitive grep filter, and a tail endpoint supports live polling from a byte offset. Every inbound event, every rejected payload, and every handler decision is written here, tagged by room where one was identified — it is the first place to look when a check-in or check-out event doesn't seem to have taken effect.
When the PMS is down
The integration fails closed. If a PMS connector is disabled, inbound HTTP events to it are rejected with a 503 rather than silently accepted, and the PMS password check fails outright when the connector isn't enabled — guests simply cannot authenticate through the PMS route while it's off, though they can still use any other login method the same journey also offers. An invalid inbound token gets a 401, and a payload the handler can't parse (wrong PMS type selected on the connector, for instance) gets a 422 — both are logged to the PMS log above so the failure is visible immediately rather than discovered when a guest complains. For a socket-based connector, a crashed daemon can be restarted directly from the connector's own admin page (a restart/status control backed by supervisorctl) without needing a deploy or a shell session.
Before you start: one account per room
A PMS connector never creates Wi-Fi accounts. It activates and suspends accounts that already exist, matching on username = room number (lower-cased). Create an account for every room first, manually or by bulk import under Accounts › Users; a check-in for a room with no account is logged as an error in the PMS log and dropped. On check-in the account is set to active, its full name becomes "First, Last" as the PMS reported it, and its password is derived from the connector's guest-password setting. On check-out it is set to suspend and the full name reverts to the room number. A room change suspends the old room's account and activates the new one, carrying the guest details across. Every processed event is also written to Reporting › Delivery Log › PMS (room, names, dates, VIP code, action, connector) — only for rooms that had an account.
The connector form
Connectors › PMS starts with Active, Name (e.g. "Hotel A", "PMS Primary") and PMS Type, grouped as the form groups them:
| Group | PMS Type | Mechanism |
|---|---|---|
| API | JSON, GET | Inbound webhook |
| Fidelio | Oracle Opera, Infor HMS, Oasis | FIAS socket daemon (Kiwire connects out) |
| Fidelio (Agent) | Oracle Opera (Agent), Infor HMS (Agent), Oasis (Agent) | Kiwire Agent on the hotel LAN relays to Kiwire's API |
| Cloud | IDB, PRO | Inbound webhook |
| POS | eZee | Inbound webhook |
| Socket | JDS : Winpac, FCS : Hospitality, 3CJ : PMSI | FIAS socket |
| Others | Rhealta, Comanche | Inbound webhook |
Which of the remaining fields appear depends on the type:
| Field | Shown for | What to enter |
|---|---|---|
| PMS Host | Socket types, IDB, GET | Hostname or IP of the PMS server for socket types (no http://); for IDB and GET, the base URL of the PMS endpoint, e.g. http://hotel.example.com/IDBPMS. |
| PMS Port | Socket types, GET | Port number used by the PMS service (placeholder 5010). The ping button next to it opens a TCP connection to host:port with a 3-second timeout — socket types only. |
| PMS Project ID | IDB, Comanche | Project identifier (required for IDB only). |
| PMS Token | IDB, Comanche, eZee | Authentication token or API key for the PMS service — used for Kiwire's outbound calls (charge posting, DB sync), not for the inbound webhook. |
| Keys | Rhealta | Rhealta credential field; the inbound webhook is still authenticated by the inbound token below. |
If you cannot pick, choose JSON: it is the generic fallback and accepts a simple JSON body from any PMS or middleware that can send an HTTP POST.
The inbound webhook
After the first save of a webhook-type connector, the form shows an Inbound Webhook block with the URL and a 40-character random token:
POST https://<your-kiwire-host>/pms/inbound/{tenant_id}The URL is per tenant, not per connector — the token is what tells Kiwire which connector (and therefore which PMS type and parser) the event belongs to. GET is accepted on the same path for the GET and PRO types. Kiwire reads the token from, in order, the X-PMS-Token header, a token field in the query string or body, or a tokenkey body field (eZee's convention). Use the header for POST-based types and ?token= for GET-based ones. IDB is the one exception: it sends no token, so an IDB connector is matched by type alone when the request carries none.
Treat the token like a password. Anyone with URL and token can push check-ins for your tenant; use the copy buttons rather than retyping it, and delete and recreate the connector if it is ever exposed (the token is generated once and kept across later saves).
The reply confirms the event was accepted and queued — provisioning runs on a background job a moment later, so check the PMS log or Delivery Log to confirm the account actually changed. On success Kiwire returns 200 {"success":true,"action":"checkin","room":"101"}; on failure {"success":false,"message":"…"} with 404 (unknown tenant), 401 (bad token), 503 (connector disabled) or 422 (the selected type could not find an action and room in the payload). Three types get a vendor-specific envelope instead: IDB receives [{"status":"1","ResponseMessage":""}], PRO receives XML (<PINS_APIResponse><Status ErrorCode="0"><Message>OK</Message></Status></PINS_APIResponse>), and Comanche receives {"Status":"SUCCESS","Identify":"…"} or {"Status":"FAIL","ErrorCode":"ERROR","ErrorMessage":"…"}.
The payload
For the JSON type, POST a JSON body with Content-Type: application/json:
| Key | Required | Notes |
|---|---|---|
action | Yes | checkin, checkout or roomchange (case-insensitive). |
room | Yes | Room number — must match an existing account's username. |
first_name or firstname | No | Either key is accepted. |
last_name or lastname | No | Either key is accepted. |
new_room | For roomchange | The room the guest is moving to. |
vip_code | No | Recorded in the Delivery Log entry. |
check_in_date, check_out_date | No | ISO 8601 or Y-m-d; recorded in the Delivery Log entry (check-in defaults to now). They do not set an expiry on the account. |
bash
curl -X POST "https://<your-kiwire-host>/pms/inbound/{tenant_id}" \
-H "X-PMS-Token: ObmY9HeFMYJr0vjKIIXoWzFprs4NNSz5xOjF5phA" \
-H "Content-Type: application/json" \
-d '{"action":"checkin","room":"101","first_name":"John","last_name":"Doe"}'The GET type takes the same fields as query-string parameters and is forgiving about names: action may also be event, type or cmd; room may be room_no, room_number, roomno or roomnumber; first name firstname, fname or first; last name lastname, lname, last or surname; new room new_room_no, newroom or newroomno; VIP vipcode, vip or voucher; dates checkin / checkin_date / arrival and checkout / checkout_date / departure. Action values check_in, arrival, ci → checkin; check_out, departure, co → checkout; room_change, move, roommove → roomchange.
Per-vendor notes
The other webhook types have fixed formats the PMS cannot change; Kiwire's parser for each one is built to match the vendor.
| Type | Request | Fields and quirks |
|---|---|---|
| IDB | JSON POST, keys UPPERCASE, no auth token | COMMAND (CHECK_IN, CHECK_OUT, INHOUSE_CHANGE), ROOM, NEWROOM, GUEST (first name), LASTNAME, ARRDATE / DEPDATE (YYMMDD), VIP (integer, 0 = none). INHOUSE_CHANGE with a NEWROOM is a room change; without one it only updates the guest name. IDB sends its JSON with a form-urlencoded content type, so Kiwire always decodes the raw body. Sync DB pulls from {PMS Host}/ws/kiwire/DBSWAP/…/{project}, authenticated with md5(token + project + timestamp). |
| PRO | GET query string, PascalCase | Event (CHECKIN, CHECKOUT, GUESTCHANGE), Room, NEWROOM, FirstName, LastName, ArrivalDate / DepartureDate (Y-m-d), VIP (empty = none). GUESTCHANGE without NEWROOM is a guest-info update. Kiwire replies in XML. |
| eZee | JSON or form POST, lowercase | mode (checkin, checkout, roommove, update), tokenkey, room, new_room, fname, lname, cidate (YYMMDD or Y-m-d), vip_code. eZee never sends a departure date, so check-out date is always empty. update only refreshes the name. |
| Rhealta | Form POST, short lowercase keys | mode (checkin, checkout, update), room, fnm / lnm, fnm2 / lnm2 and fnm3 / lnm3 for up to two sharers (parsed and kept with the event when their first name differs from the primary guest; only the primary guest is provisioned), ci (YYMMDD or Y-m-d), vip. No departure date and no room-move. |
| Comanche | JSON POST, PascalCase | Action (CheckIn, CheckOut, RoomMove), Identify (echoed back), RoomNumber (or OldRoomNumber on a move), NewRoomNumber, GuestID, GuestName (one field — stored as the first name, last name left empty), Arrival / Departure (DDMMYYYY), VIP (N or empty = none). Sync DB pulls the in-house list from {PMS Host}/wInroom/api/v1/syncroom. |
The socket daemon and the agent
Oracle Opera, Infor HMS and Oasis speak FIAS over a TCP connection that Kiwire opens to the PMS: enter PMS Host (IP or hostname, no scheme) and PMS Port (FIAS default 5010), and use the ping button to confirm the port answers before saving. Once saved and enabled, Kiwire writes a Supervisor program kiwire-pms-{tenantId}-{connectorId} running php artisan pms:socket {tenantId} --connector={id}; it reconnects every 10 seconds after a drop and sends an LA keep-alive every 60 seconds. A Socket Daemon panel appears on the form with the live status dot and a Restart button, and the hub card shows the same dot. Disabling the connector stops the program; deleting it removes the program. JDS Winpac, FCS Hospitality and 3CJ PMSI are listed under Socket and show the same host, port and ping fields, but no Supervisor program is provisioned for them and the inbound webhook has no parser for them — their handler classes exist without anything driving them yet, so do not expect events to flow from those three.
Pick an (Agent) variant when Kiwire cannot reach the PMS — typically an isolated hotel LAN. A small Windows agent runs on a machine that can reach the PMS, speaks FIAS locally, and relays parsed events to Kiwire's API (POST /api/v1/pms/event with connector_id, action ∈ checkin | checkout | roomchange, room, names and new_room; POST /api/v1/pms/sync for dumps; it polls GET /api/v1/pms/pending-action about every 30 s). The form's Kiwire Agent Setup panel walks through it: download KiwireAgentSetup-1.0.0.exe (Windows 10/11 64-bit, run as Administrator); create an Admin API key under My Profile → API Keys; save the connector once to get its Connector ID, then paste the generated config.json — server_url, api_key, tenant_id, connector_id, pms_type, pms_host, pms_port (5010), ui_port (7891) — into the agent's local dashboard at http://localhost:7891, replacing pms_host and pms_port with the real PMS address.
Guest password and sync
The Advanced Configuration block holds the guest-password rules that the PMS password check enforces at login:
| Field | Options |
|---|---|
| Guest Password Setting | Room Number As Password (default), Guest Last Name, Guest First Name, Guest Full Name or Use Predefined Password. Name modes lower-case the name the PMS sent and fall back to the room number when the PMS sent no name. |
| Use on First Login Only | Once a name-mode guest has logged in, lock the account to the password that worked rather than re-matching on every login. |
| Predefined Password | Used when the setting is Predefined; falls back to the room number if left blank. |
| Password Match % | Similarity threshold for the fuzzy name match (default 80). Lower is more forgiving. |
Sync DB appears on saved connectors of type IDB, Comanche, Opera, Infor, Oasis and their agent variants. It reconciles Kiwire against the PMS's current in-house list: every active PMS-provisioned account is suspended, then only the rooms the PMS reports as occupied are activated again with the guest details from the dump. Use it after an outage to clear guests who checked out while the link was down. For IDB and Comanche Kiwire pulls the list over HTTP straight away; for the socket and agent types it flags a pending action that the daemon (on its next keep-alive) or the agent (on its next poll) turns into a FIAS database request, and the dump arrives between the PMS's DS and DE records. A second request while one is pending is refused with "There is a pending PMS action"; the flag clears after five minutes. Other types report "DB Sync is not supported for this PMS type".