Product status
What GuestCraft does today, what is partial and what is planned. It is updated with every pull request that changes what an operator can do. Test counts and coverage are not a measure of product completeness, and a module counts as implemented only when its workflow works end to end: database, API, SDK and screens.
Last update: 1 November 2026.
Legend
Section titled “Legend”- Implemented: usable end to end by an operator or an integrator.
- Partial: usable, with the listed gaps.
- In progress: an open pull request is building it.
- Planned: designed in the analysis, not built yet.
Core (country-neutral)
Section titled “Core (country-neutral)”| Area | Status | Notes |
|---|---|---|
| Install, first operator, sign-in | Implemented | Setup code from the migration command; public sign-up stays closed. |
| Versioned releases and container images | Partial | Release automation is in place: release pull request and changelog, amd64/arm64 images on GHCR with SLSA provenance and SBOM attestations, Compose from the published images (release guide). No version has been released yet: the first release needs the owner’s one-time setup and approval. |
| Organizations, workspaces, roles | Implemented | Instance administrators create organizations and their first production or sandbox workspace; organization owners/admins create further workspaces. Workspace settings support name changes and reversible archival without deletion; Channex properties must be unlinked and webhook/installation cleanup finished before archive; saved credentials are retained. Restore leaves night-audit automation paused for business-date review; pending webhook deliveries older than 68h36m expire with retained logs and 30-day manual replay. The switcher lists reachable workspaces across organizations with user-scoped recents and an active landing preference. Workspace and organization Activity have exact filters and opaque keyset paging, and export to CSV or JSON for a range of up to 366 days, 10,000 entries and 20 MiB, each export itself audited. Sandbox uses separate data and test API keys, and connects to Channex staging; production workspaces connect to Channex production (ADR 0050). Settings also manage custom workspace roles, granular permissions, property-scoped membership/invitations, ownership transfer and account. Delegation remains bounded by live grants and property context fails closed. The organization’s counterparty directory is shared by its active workspaces (receivables), and its owners and administrators read consolidated reports across its active workspaces (reports). Workspace locale/measurement defaults and organization export/import remain separate planned capabilities. Single sign-on and SCIM are their own row below. See the tenancy guide, ADR 0049, ADR 0036 and ADR 0030. |
| Account security | Implemented | Authenticator-app (TOTP) sign-in with ten recovery codes shown once and stored as digests, regenerable, turned off with the password; web passkeys to sign in, rename and remove; per-organization security policy (second factor or passkey, grace period, remembered browsers, session age) enforced on every request with step-up when entering the organization, member compliance and the integration credentials of non-compliant creators; one-time password reset links issued by organization owners/admins or by instance administrators from the CLI, ending every session; every change audited (ADR 0053, guide). No e-mail or SMS codes; native passkeys in the mobile app are planned; single sign-on and SCIM are their own row below. |
| Enterprise identity (SSO, SCIM) | Implemented | Organization owners/admins prove domains by a DNS TXT record (one organization per domain, re-checked every 24 hours, 7 days of grace, then lapsed, with a warning; release and lapse end the SSO sessions they served) and create OIDC and SAML 2.0 connections serving them, with default workspace roles and an optional trust of the identity provider’s second factor (listed amr/acr/AuthnContextClassRef values; it meets a second-factor policy in that organization only, never a passkey one). People sign in by e-mail address only (no list of providers); OIDC always uses PKCE and a nonce; SAML requires signed assertions and refuses unsolicited responses. A first sign-in makes them members with the connection’s roles; an existing password account is linked only after its owner signs in and confirms. SCIM 2.0 tokens (shown once, hashed, expiring, revocable, audited, with the permissions chosen at creation, and budgets per address, token and organization) provision users and groups, groups map to workspace roles, and deactivation or deletion ends the membership, every session, device, pending code, remembered browser, API key and organization token of the person in the same transaction; the owner is never changed by SCIM and keeps the break-glass sign-in (ADR 0055, guide). Enforcing SSO for every member, encrypted SAML assertions, Single Logout and property-limited roles are planned. |
| Properties | Implemented | Name, time zone and currency, chosen from searchable lists. |
| Room types and rooms | Implemented | Create, edit, archive and unarchive, with occupancy limits. |
| Guests | Implemented | Search, edit, stay history, indexed strong duplicate candidates, shared-contact group review, remembered dismissal with undo, audited two-version merge with actual field choices, provenance and final survivor pointers, current consent decisions with separate histories, OTA contact notices with audited Apply, and owning-property reassignment work through API, SDK and screens. Absorbed profiles are read-only; merged identities remain operator-editable. Merge re-points every live guest foreign key in the current model; the PostgreSQL catalog test requires an explicit policy for future references. Data rights (guide, ADR 0052): retention periods per purpose in Settings → Privacy, with defaults from the workspace jurisdiction’s country pack (generic defaults keep personal data until a period is set; exports expire after 7 days) and bounded, audited jobs that also apply the technical periods (events 30 days, merge tombstones 90 days, synchronization records 30 days, outboxes emptied once sent); erasure of a guest and every profile merged into it across the profile, stays, booking revision archive, Messages content, webhook delivery logs, audit details and prepared exports, keeping accounting records, consent evidence and identifiers and stating that Channex and the OTAs keep their copies; access exports as a ZIP of JSON and CSV, downloadable once and expiring; legal holds that block erasure and retention. Every workspace column is classified in a personal-data catalog that a PostgreSQL test enforces. The Italian pack has no retention rule yet, no core store holds data collected only for an authority, attachments are exported as links to Channex, and phones keep cached reads up to 72 hours (purging them is a postponed mobile task). Legal identification remains a country-pack capability. See guest identity and ADR 0045. |
| Direct reservations | Implemented | Several rooms in one direct booking from the screens or API, with per-room type, occupancy, plan, price, availability and quote; date, room and occupancy changes, cancellation and optimistic concurrency. Groups, allotments and a public booking engine remain planned. |
| Availability | Implemented | One ledger per room type and night, including out-of-order capacity; a night consumes the type of its assigned room, so upgrades move capacity between types. The database prevents double assignment of a room, and two concurrent direct bookings cannot oversell the last room. OTA facts and a block on a type with sold stays may exceed capacity and remain visible as overbookings. |
| Today (front desk) | Implemented | Arrivals, departures, overdue departures and in-house guests, with KPIs. |
| Planning | Implemented | Rooms by night with stays, unassigned stays, availability and dated room blocks. A free range can start a booking or a block. Stays move by drag and drop or the Move dialog to another room (an upgrade when the type differs) or other nights, and split across rooms, always confirmed after a server preview (ADR 0042). In-house guests move from today; channel stays change room only. Stay edges resize with a server preview; keyboard move mode, the permissioned “Do not move” flag and ten-second inverse Undo (also Ctrl/⌘ Z) are supported. A planned move cleans the vacated room on its day, also a future day, through housekeeping tasks, and its Undo reschedules them. |
| Check-in and check-out | Implemented | Readiness checks, an audited override and same-day undo. |
| Housekeeping | Implemented | Room status (dirty, clean, inspected), single and bulk on the web, single in the mobile app, also without a network (kept on the phone and sent later, never over a newer change). Tasks: a day is generated on demand (business date to 30 days ahead), idempotently, from its stays: departure cleans, cleans of the room a planned move vacates (also on a future move day) and stayover service by the property’s setting (off, daily, every N nights); every reservation change (front desk, planning, policies, OTA import) reschedules or cancels pending tasks, and a database constraint prevents duplicates. Manual tasks (clean, deep clean, inspection, turndown, other), manual assignment one by one or in bulk to members who can work at the property, pending → in progress → done or skipped with a reason, Undo, and inspection when the property requires it (then only inspected rooms are ready for check-in). The web board per property and date shows tasks by room, arrivals first, with filters, bulk changes and live counts; the mobile app shows “My tasks” with one-tap status and a 10-second Undo, task and room detail, and fault reports. Task status, notes and fault reports with their photos made without a network wait in the app’s outbox and are sent on reconnection, each under its own key or version: a conflict is shown for a decision, never overwritten. Planning moves, resizes and their Undo reschedule the tasks in the same transaction, and every room status change publishes the unit.housekeeping_changed@1 webhook event. Planning on the web: each room’s floor or section, a roster per day (who works, from when, with a capacity in credits) and credits per task type and room type; automatic assignment per property and day, deterministic and previewed, by section, credits and capacity, keeping manual and pinned assignments, applied in one audited operation with an exact Undo. Checklist templates per task type, optionally per room type, versioned and frozen on each task when it is created; items are answered in the task detail on the web, required items gate “done”, and a failed inspection item creates a follow-up task or a fault report in the same transaction. Webhook events housekeeping_task.created@1, .assigned@1 and .status_changed@1, with identifiers only. Checklists in the mobile app and push notifications are planned (workflow, ADR 0046). |
| Rates and restrictions | Implemented | Per-room and per-person plans and derivations, nightly prices and seven restrictions, calendar editing with Undo, quotes and frozen booking prices work end to end (ADR 0027). Per-person plans price every adult count from the base occupancy’s price through a per-adult or explicit curve, shown per occupancy in the rates calendar; derived plans derive every occupancy. An infant and a child age band add nightly fees; quotes and bookings (multi-room included) take each child’s age and freeze the occupancy’s prices. Packages include catalog extras per night, per guest or per stay, frozen on each night and posted on the folio apart from the room (ADR 0029). Settings Channex cannot represent are refused at save time, and a published plan’s sale basis is fixed (distribution matrix). A sales cap (max_sell) limits the rooms a plan sells per night across every channel: its sold count comes from direct bookings and from OTA bookings attributed through the ARI mapping (unmapped OTA sales are shown as unattributed and count against no cap), quotes and direct bookings are refused at the cap unless an administrator overrides it with an audited reason (sales caps). Mandatory fees, taxes in quotes and quote expiry remain planned; policies are listed below. |
| Channex connection | Partial | A workspace connects its own Channex account in its own Channex environment: staging for a sandbox workspace, production otherwise, shown in Channels. The key is verified there only and stored encrypted; a refused key is reported as invalid, inaccessible or of the other environment, which Channex does not tell apart. A sandbox key saved before environments could be selected fails closed (environment_mismatch): no Channex call except GuestCraft’s own webhook and Messages cleanup on unlink, shown in Channels, sync health and alerts with the steps to reconnect with a staging key. The connection alone maps no property, imports no booking, publishes no ARI and activates no Messages: booking import, ARI publication and Messages are separate workflows, each with its own mapping or activation (below). |
| Channex OTA booking import | Implemented | Property/room mapping, verified webhook signals, durable revision feed/archive, acknowledgement retries, overbooking/quarantine recovery and sync health work through the front desk. Administrators import a linked property’s existing future bookings from Channels after a preview of counts and conflicts (at most the 1,000 most recent revisions per property; idempotent with the live feed, conflicts to the review queue, overbookings flagged). A booking the channel cancelled and reinstates is active again, audited and shown on the reservation, flagged as an overbooking when the rooms are gone; reinstated rooms come back without a unit. Sync failures, overbookings and quarantines are alerts on the property (Today, Planning, Reservations, Channels; an open screen refreshes them and shows each opening and resolution once as a notice) and webhook events. Active local conflicts require review. See the workflow contract and ADR 0048. |
| Channex guest messages | Partial | Members with messages.configure (built-in: administrators) activate the paid Channex Messages app per confirmed, linked and room-mapped property after an explicit cost acknowledgement, on a property where no other system installed it, and deactivate it (GuestCraft uninstalls its own installation before reporting success). An installation that Channex never confirmed to GuestCraft is neither adopted nor removed: members with messages.configure confirm it as GuestCraft’s or leave it alone. Threads arrive through the property-filtered Channex live feed; a foreign item stops the property with a visible health state. Front desk reads a shared inbox with undecided requests first and their deadlines, pages back through a conversation’s whole stored history (it opens on the latest 1,000 messages; “Load older messages” loads the rest, retention and erasure placeholders included), sends replies whose variables the server resolves, sends attachments through an outbox that never sends a reply twice, and uses and edits templates per language. Requests are decided from GuestCraft before their deadline, behind the stop barrier: booking and alteration requests are accepted or declined, inquiries are pre-approved, answered with a special offer or left to lapse; a decision made meanwhile in the channel’s extranet wins, and a late one is refused. A workspace retention setting (default and per purpose) is applied by a bounded job that redacts expired content and keeps thread metadata; members with messages.erase erase a guest’s message content, and the guest data erasure includes it (guest data rights). Unlink stops Messages locally, cancels queued replies and removes GuestCraft’s own installation and webhook. Upstream limits: attachments on inquiry threads without a booking work against the simulator, but Channex documents attachment identifiers only for booking threads, so live support awaits certification; inquiries carry no authoritative deadline, so a lapse needs a known deadline or channel outcome; conversations without a booking have no trusted guest link and are left to retention (ADR 0044). |
| Channex ARI push (availability, prices, restrictions) | Partial | Plan mapping, explicit takeover, absolute ledger/rate publication, numbered leases, booking barrier, shared pacing, horizon and health/resync work through Channels. Vercel requests wake bounded pumps after commit, with the daily cron as a safety net; Docker keeps its resident worker. Automatic historical/future-booking catch-up, bounded unknown-outcome repair, per-property holds, direct-only plans and audited health repair are implemented. A lost send is settled by provider request lookup when Channex holds its values, otherwise by the bounded repair; a nightly check from 02:00 property time compares Channex with the published values and republishes only the differences, visible in Channels → health; sync failures and unrepaired drift become alerts on the property and webhook events (no e-mail). Channex’s certification scenarios are rehearsed on a simulated Channex staging from a sandbox workspace, in CI and with pnpm --filter @guestcraft/server certification, which writes an evidence pack of each scenario’s calls, payloads and task IDs; the certification session with Channex staff on staging is the owner’s action; per-person plans publish every adult count in Channex’s rates format after their Channex options and child fees are verified, with drift checked through the main occupancy only (what each OTA shows is unverified); each plan’s sales cap is published as its remaining max_sell, read back and repaired on drift, and Channex’s channel availability rules are shown read-only (whether Channex counts its own bookings against max_sell is a certification question); a live rate plan or room type is retired or replaced through a verified workflow that stops it over the horizon, reads Channex back and only then switches the mapping, with bookings on a retired room type held for review (a live check of the closure with Channex staff is part of certification) (workflow, certification checklist, ADR 0034, ADR 0048). |
| Folio and payments | Partial | Append-only reservation account with frozen room charges, catalog extras (canonical tax categories), manual deposits/payments/refunds and an audited tax re-rate. Packages: a rate plan’s included services are posted per night apart from the accommodation, with their own tax categories, are never charged again as extras unless explicitly beyond the package, and appear as their own revenue category. Split folios: up to ten folios per reservation, each with its payer (the booker, another guest or a company with billing details), routes for room nights, catalog extras or tax categories, transfers between folios recorded as transfer pairs that net to zero (folios.transfer), closing a settled folio, closing every open folio at the last check-out (undone with the check-out) and reopening (folios.split). Per-room settlement: a folio attached to one room of a multi-room booking is settled when that room checks out, while the last room settles the reservation; credits never hold check-out and owed balances need payment or an audited folios.approve_open_balance approval. Bulk re-rating of selected nights, rooms or reservations at the current rates with a per-stay preview, under the frozen-price and version rules. Balances, non-fiscal statements (per folio or per reservation) and the refunds-due list per folio, through API, SDK and screens. Online payments through the workspace’s own Stripe account (Settings → Payments, restricted key and webhook signing secret, sealed): payment links for a prepayment, the balance, a fee, a damage deposit or a card guarantee, copied or shown as a QR code (no e-mail or SMS), with their own expiry (7 days by default) and one open per folio and purpose; Stripe’s signed events, or GuestCraft’s reads of Stripe, are the only way a payment is settled, and each settled payment or refund posts exactly one card_online folio movement, shown in the cash and finance reports; saved cards charged without the guest after confirmation, damage-deposit holds with their expiry, captured with the damage charge or released, provider refunds from refunds due, disputes (a lost one makes the payment owed again). OTA cards: each Channex booking’s payment facts (who collects, and its card reduced to brand, last four digits, expiry and a virtual card’s currency, balance and dates; never its number or security code) are kept from its latest revision; once the operator installed the property’s Channex Stripe Tokenization App in Channex and acknowledged it in GuestCraft with its cost (a stop fences locally; Channex’s 403 shows the app missing; a booking without known last four digits is never matched), staff move the card to Stripe and charge it off-session, a virtual card only within its dates, currency and balance, never an Airbnb booking’s. Cash settlement of a whole balance or credit applies the property’s country-pack cash rounding as its own linked line. A company payer linked to a counterparty closes its folio by moving the owed balance to the counterparty’s open item, and reopening reverses it exactly (receivables). The TaxCalculator port is ready for country modules but no tax rules, fiscal documents, bulk tax recalculation or payment terminals exist yet (workflow, online payments, bulk re-rating, ADR 0029, ADR 0054). |
| Receivables | Implemented | An organization directory of companies, agencies and OTAs (billing details, tax identifiers as text, payment terms, currency, optional credit limit as a warning, contacts), shared by the organization’s active workspaces under receivables.manage, never granting organization administration. Linked company folios close to open items at the desk (receivables.transfer, built in for the front desk, also when limited to its properties) when the counterparty allows direct billing, with frozen payer, terms and due date; reopening reverses the transfer exactly while nothing is applied. Receipts by bank transfer, card, cash (joining the desk’s cash drawer) or other are allocated in part or across items, leave available credit, and can be released or voided; write-offs need receivables.write_off and a reason. OTA-collected Channex bookings open expected payouts of the total less the commission Channex supplies, otherwise with the commission shown as unknown, amended by later revisions. Current aging per currency in each property’s time zone, a non-fiscal counterparty statement with CSV, and a counterparty’s exposure (organization-wide for owners and administrators). Every change is audited and emits receivable.*@1 events. A receipt pays only its own workspace’s items; payout ingestion from OTAs, bank reconciliation, dunning, historical aging and fiscal invoices or credit notes are not built. See receivables and ADR 0051. |
| Cancellation policies, penalties, no-show | Partial | Property policies, frozen terms, simulated cancellation fees/waivers (also for the remaining rooms of a multi-room booking), no-show with inventory release, a fee waiver (policies.waive_no_show) and same-day recovery, and manual guarantee/prepayment tracking work through API/SDK/screens (ADR 0032). Tentative options are fee-free; migrated bookings carry unconfirmed conditions. A card saved through a payment link confirms the card guarantees still pending (shown as confirmed by a saved card); a per-property Payments due list follows the frozen prepayment schedule in property time, where staff charge a saved card after confirmation or create a link, with failure reasons; posted cancellation and no-show fees can be collected from the saved card; Airbnb bookings refuse off-platform collection (online payments). An OTA booking’s no-show, invalid card and cancellation over an invalid card are reported to Booking.com/Expedia through Channex within each channel’s window computed in the property’s time zone, once per reservation and kind, with operator reconciliation of a lost answer, through API/SDK/screens (booking reports); verified on the Channex simulator only, a live check remains. |
| Night audit and business date | Partial | Manual and automatic property audits share durable/resumable runs, policy-engine automatic no-shows, fee-free expired tentative options, exact room-charge reconciliation, one-day advancement and persistent execution health/lag recovery through API/SDK/screens. Cash drawer shifts cover opening float, server-linked cash postings/refunds/corrections, counted closure with discrepancy reason, history/report and optional enforcement. Docker dispatches every minute; ordinary Vercel requests wake due audits after the response, with a daily cron safety net (without traffic, work waits for that trigger). Allotment release and fiscal closure remain planned (workflow, cash drawer, ADR 0043). |
| Out-of-order and out-of-service rooms | Implemented | Dated blocks appear in Planning and Rooms. Out-of-order reduces availability and enqueues ARI changes; out-of-service prevents physical assignment while leaving saleable availability intact. Outbound ARI delivery uses the guarded Channex publisher; see its partial capability above. |
| Maintenance tickets | Implemented | Priority, assignee, scheduled and due dates, open → in progress → done with reopening, the room and its block, and an activity history, through the web list and detail panel. Fault reports from a task or a room, on the web and in the mobile app, with photos (downscaled on the device, metadata removed, stored in the database) and optionally an out-of-order or out-of-service block under the block rules. Closing a ticket never makes the room clean: it creates a clean or inspection task. Fault reports and their photos made without a network wait in the mobile app’s outbox and are sent on reconnection, the report first, each under its client identifier. Photos are kept in the database by default, or, when the instance configures it, in object storage (an S3-compatible bucket such as AWS S3, Cloudflare R2, MinIO or Supabase Storage, or a private Vercel Blob store), always downloaded through the API’s permission checks; a job collects objects left without a row, and a command moves the stored bytes out of the database and back. Automatic photo retention: Settings → Privacy sets a period for the photos of tickets closed that many days ago (kept until a period is set); the hourly, bounded and audited retention job removes their stored bytes and then their rows, except for tickets reported from the stay of a guest under a legal hold (guest data rights, workflow, ADR 0046). |
| Groups and allotments | Planned | |
| Online check-in | Planned | Guest self-service remains planned. |
| Reports | Implemented | Per-property performance with historical room/type/OOO capacity and labelled baseline inventory before capture; daily pickup rooms/revenue between two captured observation dates; cash payments/refunds by method and service date; gross folio revenue by category/tax category and service date. Every property report has chart, table and CSV under reports.view (consolidated reports have tables and CSV), URL filters, denied access and recovery. Cancellation trends by cancellation date, lead time, channel and cause from the capture start (earlier cancellations count as unknown); workspace multi-property consolidation and organization consolidation for owners and administrators, per currency or converted with stored exchange rates; receivables aging and counterparty statements. Pre-baseline reservation snapshots are unavailable; country reports and owner statements remain planned. See reports and ADR 0031. |
| Owners and statements | Planned | For property managers. |
| Booking engine | Planned | |
| Mobile app | Partial | An Expo app (apps/mobile) connects to any instance by its address through discovery, or by scanning the QR code of Settings → Account → Pair a phone (a one-time code valid two minutes that gives the phone its own named device session; used or expired codes are refused), or signs in through the browser; device sessions are listed and revocable in Account settings. It offers a workspace and property picker; Today; search of reservations and guests; the reservation detail (stay, rooms, guests, status, balance) with room assignment, check-in with readiness checks and the audited override, check-out under the balance rules and same-day undo; and room status changes. Reads of these screens are kept on the phone for 72 hours, partitioned by instance, account and workspace, kept out of device backups, shown offline with their time (each read inside a screen with its own), and deleted on sign-out, revocation and instance removal, with reads in flight aborted and late saves refused; room status changes made offline wait in an outbox and are sent on reconnection without overwriting a newer change. Functions the instance does not declare (including the stay actions without front_desk) and actions the role refuses are hidden or disabled with the reason. No store release exists yet; push notifications, branded builds, native passkeys, check-in with identification and payment links are planned; housekeeping and maintenance notes, task status and fault photos offline are the Housekeeping row’s, registered in the same outbox (ADR 0041). |
| Public API, OpenAPI, TypeScript SDK | Partial | Covers every module above that is implemented, with public discovery (/.well-known/guestcraft), device sessions for native apps and Seam’s three levels of integration credentials: workspace API keys, organization tokens and client sessions for front ends (`createGuestCraft({ baseUrl, apiKey |
| API keys | Implemented | Members with api_keys.manage (built-in: administrators) create keys in Settings → API keys (gc_live_…, or gc_test_… in a sandbox workspace) with some of their own permissions, all or some properties, an optional expiry and an optional allow-list of up to 20 client address ranges, copy the secret once, see creator, last use, status and whether the creator still holds the key’s grant, rotate a key (the replacement’s secret is shown once; the old key works for a grace period of 0–168 hours) and revoke them (401 from the next request). A key never exceeds its creator’s live permissions, never reaches team, key, webhook or Channex administration, and gets 600 requests a minute per instance while the instance remembers it (up to 10,000 keys and 100 of one workspace, each until a day unused; a key that finds no free place is refused 429 until one frees); a key not remembered on an instance is looked up one at a time per client address and only while the instance has fewer guesses being looked up than half its auth pool’s connections, is refused at once, not queued, when it finds no permit (rarely admitted under a large flood of guesses), and is refused before any lookup once its address failed 30 times in a minute or its IPv6 /48 300 times (neighbours within a /48 share that budget), while a remembered key is never refused for others’ failures (all of it per instance). Requests of one key sent while it is being looked up wait for that lookup and get its answer, so a burst of first requests is answered. The mobile app’s device tokens are admitted by the same budgets, so its parallel first reads after a restart succeed. A request from an address outside a key’s allow-list (compared on the whole address, all 128 bits for IPv6) answers 401 and never affects the allowed client’s requests. Creation, rotation, revocation and refused uses are audited, and every persisted write of a key is audited as the key acting for its creator (Messages read markers included, which members move unaudited as the team’s working state). An organization holds at most 1,000 of the instance’s 10,000 remembered places across all its credentials, so creating workspaces never multiplies its share. Budgets and remembered keys are per instance (ADR 0040). |
| Organization tokens | Implemented | Owners and administrators of the organization create gc_org_… tokens in Settings → Organization tokens with some of the permissions an integration can hold, all or some of the organization’s workspaces, an optional expiry and an optional allow-list of up to 20 client address ranges, copy the secret once, see creator, last use, status and what the creator still holds, rotate them with a grace period of 0–168 hours and revoke them. A request chooses its workspace with GuestCraft-Workspace-Id and holds there the token’s permissions that its creator still holds; another workspace, or none while it reaches several, answers 403 and is audited. Same budgets, audit actor (organization_token, shown in the workspace’s Activity) and browser-only administration as API keys; tokens take their organization’s share of the remembered keys. Their administration and refusals are audited at organization level and listed in the organization’s Activity (guide, ADR 0047). A token can also hold organization permissions, workspaces.create and organization_audit.view (organization Activity), which its creator must hold live as owner or administrator; a token with only those calls workspace creation and organization Activity without any workspace, and never administers the organization. Other organization permissions are planned. |
| Client sessions | Implemented | An integration’s server mints, with an API key or organization token holding client_sessions.manage, a gc_cst_… token for one of its user identifiers (never personal data), some permissions on 1–100 properties of one workspace and at most 24 hours (one by default); a front end calls the API with it alone, from any origin (CORS preflight answered; no answer authenticated by a cookie is readable across origins). On every request it holds only what its minting credential may still do, and answers 401 once it, or that credential, is revoked or expired. Members and integrations with the permission list and revoke sessions, also in Settings → Client sessions; uses are audited as client_session. At most 100 sessions of one workspace are remembered per instance within a minute. A session can be limited to one reservation, one guest or one property’s public booking (quotes and new bookings), with a fixed set of permissions per kind: it then reaches only the operations that name that object, and everything else answers 403, audited; a merged guest’s session stops working. First-party guest sessions (gc_gst_…, at most an hour, the guest permission vocabulary) are issued in process for the guest-facing pages of later modules and reach no staff operation; no page uses them yet (guide). |
| MCP server | Implemented | Each instance serves an MCP server at /api/mcp (stateless Streamable HTTP, on Vercel and Docker): MCP clients discover it through the OAuth challenge and its RFC 9728 metadata, register themselves and get a token bound to it after a member’s consent; API keys and organization tokens work too. Its tools are every operation an integration may call, generated from the instance’s OpenAPI document, plus availability and quotes, finding a reservation, booking changes, check-in and check-out readiness and room status; each call runs in process as the caller, never beyond their live grant. Destructive tools change nothing on their first call: the client’s form elicitation, or a one-time confirmation token, approves exactly what was previewed, once across every instance. @guestcraft/mcp (MIT, not published) also runs over stdio and stand-alone Streamable HTTP with a key (setup for MCP clients). |
| Outbound webhooks | Implemented | Members with webhooks.manage (built-in: administrators) register HTTPS endpoints for the event catalog (reservation changes and overbookings, Channex booking imports, folio postings, payments, refunds, re-rates, closing and reopening, payment links succeeded, failed or expired, rate plans and rate changes, room housekeeping status, housekeeping tasks created, assigned and changing status), copy the signing secret once, rotate it (the previous one signs for a day, then is forgotten), edit, test, disable and delete endpoints, read the 50 most recent deliveries, redeliver one, and replay an endpoint’s events of a range within the last 30 days (at most 1,000 per replay). Members and API keys with events.read read the same events of the last 30 days in Settings → Events and through GET /v1/events. Events carry identifiers only, signed with Standard Webhooks headers (webhooks.verify in the SDK); the worker retries for about 69 hours and disables an endpoint that fails that long; previews and restored copies never send. Private and local addresses are refused, also when a name resolves to one at connect time. Signing secrets follow ENCRYPTION_KEYS rotation. Each endpoint can choose the version of each event it receives (GET /v1/event_types); a version is current or deprecated with an announced date, nothing is retired automatically, and a queued delivery or a redelivery keeps the version it was queued with. Each workspace has one delivery job at a time, and a failing endpoint backs off and takes at most half of each batch, so it never holds up another endpoint. Events older than 30 days are not kept (ADR 0040). |
| Languages and themes | Implemented | English; light, dark and high-contrast themes; three densities. |
Country modules
Section titled “Country modules”Country-specific rules are add-on modules, outside the core (ADR 0015). The core exposes generic extension points, such as tax calculation, guest registration requirements, fiscal documents and authority reports, and a country module implements them.
| Country | Module | Status |
|---|---|---|
| Italy | Cash rounding and consumer price rules | Implemented (experimental pack) |
| Italy | Alloggiati Web, ISTAT, tourist tax, e-invoicing (FatturaPA) and RT | Planned; needs validation by an Italian professional |
Country-pack workflow consumption
Section titled “Country-pack workflow consumption”Cash rounding (payments.cash_rounding) is consumed by cash settlement: settling a
folio’s whole balance or credit in cash rounds the total with the pack of the property’s
country for the payment date, and books the difference as its own linked line that the cash
drawer never counts (online payments).
Partial cash payments are not rounded. Consumer prices (prices.consumer) are planned: not
yet consumed by pricing workflows: what a consumer price must include is a legal question.
Guest ownership and deployment compatibility
Section titled “Guest ownership and deployment compatibility”Standalone guest creation requires an owning property; existing ownership
is preserved on edit. New OTA guests inherit the booking property. Legacy unowned guests
are not backfilled automatically. Unrestricted administrators with guests.reassign can
explicitly correct the owning property using the guest version; the change is audited.
See guest identity.
Deployment schema checks accept a newer journal only when every extra migration is
declared expand; the runtime’s own checksum and kind prefix must still match. This
supports cold starts after an additive failed or unpromoted candidate and Instant Rollback.
A contract migration requires roll-forward. Runtimes built before this compatibility
change still enforce strict journal equality; the SQL linter and grant floor are conservative
guards, with application-level compatibility still subject to review.