@guestcraft/sdk
Questi contenuti non sono ancora disponibili nella tua lingua.
A portable TypeScript client for your GuestCraft instance. MIT licensed, built on standard fetch, and generated from the repository’s OpenAPI contract. Hey API generates its operations, types and Zod validators; Zod is its runtime dependency. React query factories live separately in @guestcraft/react.
This is a private pnpm workspace package, version 0.0.0. It has not been published to npm. Its exports currently point to TypeScript source, so your workspace consumer needs to support TypeScript, as this repository does.
Use it in this monorepo
Section titled “Use it in this monorepo”From the repository root, add the package to a workspace consumer:
pnpm --filter <your-workspace-package> add @guestcraft/sdk@workspace:*In a browser already signed in to the same GuestCraft instance:
import { createGuestCraft } from "@guestcraft/sdk";
const guestcraft = createGuestCraft({ baseUrl: window.location.origin, workspaceId: "ws_01k8b4c7k9f5qr7v1d2s3x4y5z", // Your dashboard’s workspace ID.});
await guestcraft.health.get(); // { status: "ok" }
const { data: connection } = await guestcraft.channex.getConnection();if (connection.connected) { const { data: properties } = await guestcraft.channex.listProperties(); // [{ id: "channex-property-id", title: "Hotel Aurora" }]}baseUrl is an absolute HTTP(S) instance root, such as http://localhost:3000 or https://host.example/guestcraft. The SDK preserves that deployment prefix and appends /api/v1. Do not include /api/v1, credentials, a query or a fragment in baseUrl.
Authentication and workspace selection
Section titled “Authentication and workspace selection”GuestCraft authenticates browser cookie sessions, native device sessions (a device token of the mobile app, see Device tokens below) and workspace API keys for integrations (see API keys below). For a cookie session, sign in through the instance’s /sign-in page or POST /api/auth/sign-in/email. HTTPS uses __Secure-guestcraft.session_token; HTTP localhost uses guestcraft.session_token. Your Channex API key is not a GuestCraft credential.
The SDK sets credentials: "include". Same-origin browser requests use the browser’s session; the SDK cannot read the HTTP-only cookie. Node.js fetch does not manage a cookie jar automatically. On a server, inject a cookie-aware fetch, or explicitly forward the current user’s trusted session cookie:
const guestcraft = createGuestCraft({ baseUrl: "https://guestcraft.example", workspaceId, // Obtain this from the current authenticated server request, never a public input. headers: { Cookie: currentUserSessionCookie }, fetch: globalThis.fetch,});Create a client per user/session and workspace. Do not share a cookie-bearing client across users. headers are copied on construction; use them for Accept-Language or a request ID as well. workspaceId sets GuestCraft-Workspace-Id and overrides any workspace header in headers. It selects a workspace the user already has access to. It may be omitted when that user has exactly one workspace. Cross-origin browser use also depends on the instance’s origin and cookie policies.
Device tokens
Section titled “Device tokens”A native app signs in through the browser and receives a device token; see discovery and device sessions. Pass it as a header:
import { createGuestCraft, discoverInstance } from "@guestcraft/sdk";
const instance = await discoverInstance("https://hotel.example.com"); // /.well-known/guestcraftconst guestcraft = createGuestCraft({ baseUrl: "https://hotel.example.com", headers: { Authorization: `Bearer ${deviceToken}` }, // from the keystore, never logged fetch,});const { data: workspaces } = await guestcraft.workspaces.list();discoverInstance throws a GuestCraftError: http (a 404 means no discovery), network, or
invalid_response. deviceSessions.create, list and revoke and deviceAuthorizations.create
cover the rest of the flow; instance.get reads discovery at /api/v1/instance. To pair a phone
with a QR code instead, a signed-in browser calls devicePairings.create(), shows its
pairing_url, polls devicePairings.get until status is no longer pending, and withdraws the
code with devicePairings.cancel when it stops showing it; the app sends { pairing_code, device_name } to deviceSessions.create.
API keys and webhooks
Section titled “API keys and webhooks”A member with api_keys.manage creates a workspace API key in Settings → API keys and copies its secret,
shown once: gc_live_… in a production workspace, gc_test_… in a sandbox one. The key acts in
its workspace only, with the permissions and properties it was given, never more than its
creator still holds; see API keys and webhooks.
import { createGuestCraft } from "@guestcraft/sdk";
const guestcraft = createGuestCraft({ baseUrl: "https://hotel.example.com", apiKey: process.env.GUESTCRAFT_API_KEY, // from a secret store, never logged});const { data: properties } = await guestcraft.properties.list();With apiKey the client sends Authorization: Bearer and no cookie. A revoked or expired key
answers 401 auth.unauthenticated; more than 600 requests a minute answer
429 auth.rate_limited with Retry-After, and so do a key’s requests while the instance has no
place to count them (it remembers 10,000 keys, 100 per workspace, each place freed a minute after
its key’s last use). API keys and webhook endpoints are administered from a browser session only, through
guestcraft.apiKeys, webhookEndpoints and webhookDeliveries, or with the generated
operations (apiKeysCreate, webhookEndpointsUpdate, webhookDeliveriesList, …) sent through a
transport (see Send only the operations you use):
import { createGuestCraftTransport } from "@guestcraft/sdk";import { apiKeysCreate } from "@guestcraft/sdk/operations/api-keys";
const transport = createGuestCraftTransport({ baseUrl: window.location.origin, workspaceId });const issued = await transport.send(apiKeysCreate, { body: { name: "ERP", permissions: ["reservations.read"] },});Organization tokens
Section titled “Organization tokens”An owner or administrator of the organization creates an organization token in Settings →
Organization tokens and copies its secret, shown once (gc_org_…). It acts in the workspaces of
the organization it names (all of them by default), one per client, with its permissions narrowed
in each workspace to what its creator still holds there; see
API keys and webhooks.
const headquarters = createGuestCraft({ baseUrl: "https://hotel.example.com", organizationToken: process.env.GUESTCRAFT_ORGANIZATION_TOKEN, // from a secret store workspaceId: "ws_01k8b4c7k9f5qr7v1d2s3x4y5z", // required when the token reaches several});const { data: workspaces } = await headquarters.workspaces.list(); // every workspace it reachesCreate one client per workspace, or pass headers: { "GuestCraft-Workspace-Id": … } to a single
call. Tokens are administered from a browser session only, through guestcraft.organizationTokens.
Client sessions
Section titled “Client sessions”Like Seam’s client session tokens, a client session lets a front end, such as a guest page or an
embedded panel, call the API with no other credential. Your server mints it with its API key or
organization token (client_sessions.manage), for one of your users and a narrow grant: some
permissions on some properties of one workspace, or within one reservation, one guest or one
property’s public booking (scope), for at most 24 hours (one hour by default).
// On your server, with the API key or organization token.const { data: issued } = await guestcraft.clientSessions.create({ body: { user_identifier: "guest-account-1042", // your identifier, never an e-mail address or a name permissions: ["properties.read", "reservations.read"], property_ids: ["prop_01k8b4c7k9f5qr7v1d2s3x4y5z"], },});// Hand issued.token (gc_cst_…) to the front end, for example in the page you render.
// In the front end, with the session's token alone: no cookie, no key.const session = createGuestCraft({ baseUrl: "https://hotel.example.com", clientSessionToken: tokenFromYourServer,});const { data: reservations } = await session.reservations.list({ query: { property_id } });The session’s requests may come from any origin: the API answers the CORS preflight and lets the
page read the answers of requests carrying a client session token. A session holds on every request
only what its minting credential may still do, and ends when that credential is revoked or
expires; clientSessions.revoke ends it at once. An expired or revoked session answers
401 auth.unauthenticated: ask your server for a new one.
A page for one guest mints a narrower session, which reaches only the operations that name its
object; any other answers 403 auth.forbidden:
const { data: issued } = await guestcraft.clientSessions.create({ body: { user_identifier: "booking-4711", permissions: ["reservations.read"], scope: { kind: "reservation", property_id: "prop_01k8b4c7k9f5qr7v1d2s3x4y5z", reservation_id: "rsv_01k8b4c7k9f5qr7v1d2s3x4y5z", }, },});Webhook verification
Section titled “Webhook verification”A webhook receiver verifies each request with the endpoint’s secret (whsec_…) before it trusts
the body. Pass the body exactly as received:
import { WebhookVerificationError, webhooks } from "@guestcraft/sdk";
export async function POST(request: Request) { const body = await request.text(); try { const event = await webhooks.verify(body, request.headers, process.env.GUESTCRAFT_WEBHOOK_SECRET); // event.id is the webhook-id: skip it if you processed it already. // event.data names the object; read it again with your API key. } catch (error) { if (error instanceof WebhookVerificationError) return new Response(null, { status: 400 }); throw error; } return new Response(null, { status: 204 });}verify follows Standard Webhooks: it refuses a missing header, a timestamp more than five
minutes away (toleranceSeconds) and a body that no v1 signature matches, comparing in
constant time; during a secret rotation either secret’s signature is accepted.
Send only the operations you use
Section titled “Send only the operations you use”createGuestCraft references every operation it wraps, so a bundle that uses it carries them all.
A browser app that cares about its size sends the generated operations through a transport
instead, and imports them from their OpenAPI tag’s module:
import { createGuestCraftTransport } from "@guestcraft/sdk";import { reservationsUpdate } from "@guestcraft/sdk/operations/reservations";
const transport = createGuestCraftTransport({ baseUrl: window.location.origin, workspaceId });const reservation = await transport.send(reservationsUpdate, { path: { property_id: propertyId, id: reservationId }, headers: { "if-match": etag }, body: { notes: "Late arrival" },});createGuestCraftTransport takes the options of createGuestCraft. send answers the data of a
successful response and throws a GuestCraftError otherwise, like the facade. Its input has the
operation’s own types: a missing required field, a body of another operation or a wrong expected
answer does not compile. No generated operation asks for the workspace header, because the client
sends the one its transport was created with; if-match stays required where the operation needs
it. transport.client is the generated client, for calling an operation directly or for the query
factories of @guestcraft/react. When you call an operation directly, pass that client, or set
GuestCraft-Workspace-Id on the request yourself: the default generated client sends no workspace,
and the API refuses a workspace operation without one, although the compiler does not ask for it.
propertiesFacade(transport) and channexConnectionFacade(transport) are the facade’s two groups
that check their input before a request. channexConnectionFacade is also its own module,
@guestcraft/sdk/channex-connection, so that a caller can load it only when needed.
Each OpenAPI tag has its module: @guestcraft/sdk/operations/<tag>, with the tag in kebab case
(reservations, reservation-rooms, api-keys, …). The module carries the tag’s operations and
the response validators only they use; what several tags use is in a shared module. A bundle
keeps every module that a re-exporting module names, so import a tag module rather than
@guestcraft/sdk/operations, which re-exports every tag, or the root’s operations namespace;
both remain for existing code. The tags and their operations are in the
API reference.
Transport problem validation uses its own generated schema module. It does not initialize
shared guest or reservation response schemas on screens that only need the transport.
Import GUEST_CONSENT_PURPOSES from @guestcraft/sdk/guests; this runtime catalog stays
outside the root transport entry point.
Operations
Section titled “Operations”The createGuestCraft facade exposes every operation in the committed OpenAPI contract. Methods
are grouped as follows; each method takes the generated operation input, except the no-input operations
health.get, instance.get, organizations.list, workspaces.list, deviceSessions.list, devicePairings.create,
and webhookEndpoints.list, which take only { signal }, the checked properties, and the Channex
connection helpers getConnection, connect, and listProperties documented below.
health:getinstance:getdeviceAuthorizations:createdevicePairings:create,get,canceldeviceSessions:create,list,revokeorganizations:list,create,audit,auditExportCsv,auditExportJsonaccountSecurity:getPolicy,updatePolicy,compliance,issuePasswordReset,withdrawPasswordReset,redeemPasswordResetenterpriseIdentity:listDomains,addDomain,getDomain,verifyDomain,releaseDomain,listConnections,createConnection,getConnection,updateConnection,deleteConnection,listScimTokens,createScimToken,revokeScimToken,listScimGroups,updateScimGroupAccess,listScimUsersworkspaces:list,create,get,update,setArchivalnightAudits:context,settings,updateSettings,warnings,reviewWarnings,precheck,list,create,get,completecashShifts:context,current,list,create,get,closecashShiftSettings:get,updatepaymentConnection:get,update,verify,delete,retryDeliveries(browser session only)reservationPayments:getpaymentRequests:create,get,cancelcardCharges:createotaCards:moveotaCardCharges:createcardTokenization:get,acknowledge,stop(browser session only)providerPayments:get,capture,releaseproviderRefunds:createpaymentsDue:listcashSettlements:createcounterparties:list,create,get,replace,exposure,statement,statementCsvreceivableItems:list,get,writeOffreceivableEntries:releasereceivableReceipts:list,create,get,allocate,voidreceivables:aging,agingCsvteam:list,invite,resend,revoke,changeRole,remove,customRoles,createRole,audit,auditExportCsv,auditExportJson,accept,getRole,updateRole,deleteRole,getAccess,changeAccess,getSignInLock,unlockSignIn,roles,transferOwnershipapiKeys:list,create,get,revoke,rotateorganizationTokens:list,create,get,revoke,rotateauthorizedApplications:list,get,revokeclientSessions:list,create,get,revokewebhookEndpoints:list,create,get,update,delete,rotateSecret,test,replaywebhookDeliveries:list,redelivereventTypes:listevents:list,getblocks:list,create,update,remove,endmaintenanceTickets:list,create,get,update,status,activitymaintenanceTickets.photos:create,get,removemaintenanceAssignees:listhousekeepingSettings:get,updatehousekeepingDays:get,generatehousekeepingTasks:list,create,get,update,status,assign,bulkStatus,bulkAssignhousekeepingAssignees:listhousekeepingRosters:get,updatehousekeepingCredits:get,updatehousekeepingAssignmentPlans:gethousekeepingAssignmentRuns:create,list,get,undohousekeepingChecklists:list,create,get,update,removehousekeepingTaskChecks:list,answerratePlans:list,create,update,archiverates:get,apply,restorequotes:createreports:history,performance,performanceCsv,pickup,pickupCsv,cash,cashCsv,revenue,revenueCsv,cancellations,cancellationsCsv,consolidated,consolidatedCsv,organizationConsolidated,organizationConsolidatedCsvexchangeRates:list,create,deleteroomTypes:list,create,get,update,archive,unarchiveunits:list,create,update,get,archive,housekeeping,bulkHousekeeping,unarchivereservationRooms:undoCheckIn,undoCheckOut,update,assign,move,split,resize,setDoNotMove,undo,checkIn,checkOut,markNoShow,undoNoShow,waiveNoShow,guarantee,checkoutBalanceguests:get,update,reservations,create,list,duplicates,guestDuplicates,dismissDuplicate,merge,consents,recordConsent,reassignOwnership,sharedContacts,guestSharedContacts,sharedContactGuests,merges,consentCurrent,contactNotices,dismissContactNotice,undoDuplicateDismissalguestDataRetention:get,updateguestLegalHolds:list,create,releaseguestErasure:createguestDataExports:createguestDataRequests:list,get,downloadreservations:create,list,get,update,cancel,simulateCancellation,prepayments,listOtaReports,reportToOta,reconcileOtaReportpolicies:list,create,updateavailability:getcalendar:getfrontDesk:getfolios:get,postExtra,pay,rerate,refund,reverse,transferreservationFolios:list,get,create,update,close,reopen,linkCounterparty,unlinkCounterpartyrerates:applyfolioExtras:list,create,updatefolioTaxCategories:listfolioCreditsDue:listchannex.ari:health,plans,mappings,map,repair,resyncchannex.ari.retirements:create,get,cancel,resumechannex.bookingHistory:preview,import,listchannex:alerts,alertTransitions,sync,roomTypes,map,candidate,unlink,forgetRemoval,registerWebhook,requestDrain,retryRevision,getConnection,connect,patchConnection,listPropertieschannexMessages:list,get,put,confirmInstallation,forgetInstallation,synchronizemessageThreads:list,summary,get,messages,putReadState,variables,reply,attach,outbox,retry,dismiss,noReplyNeededmessageRequests:list,get,decidemessageRetention:get,updatemessageGuests:eraseContentmessageTemplates:list,create,get,update,deleteproperties:get,update,list,create
organizations.list, create, audit, auditExportCsv and auditExportJson, and workspaces.create, get, update and
setArchival require a browser session. Their scope comes from the signed-in person and
resource path, so they do not need workspaceId on the client. Creating an organization
requires instance administration; creating or archiving a workspace requires organization
ownership or administration. Workspace reads and name changes require workspaces.configure
with access to all properties. Pass the returned strong ETag in headers: { "if-match": etag }
for update and setArchival. Archival retains saved connections and data, and requires
linked Channex properties and unfinished cleanup to be settled first. See the
organization and workspace contract.
These methods use the generated OpenAPI request/response types. For blocks, dates are half-open
property-local ISO dates; out_of_order and available are returned by availability reads.
See ADR 0033.
| SDK method | HTTP operation | Result |
|---|---|---|
health.get(options?) |
GET /api/v1/health |
{ status: "ok" }; public process liveness only |
properties.list({ limit?, cursor?, signal? }?) |
GET /api/v1/properties |
{ data, next_cursor, has_more } |
properties.create(settings, options?) |
POST /api/v1/properties |
New local property, HTTP 201 |
properties.update(id, settings, options?) |
PUT /api/v1/properties/{id} |
Saved local property |
properties.get(id, options?) |
GET /api/v1/properties/{id} |
Raw local Property with id, workspace_id, name, time_zone, currency |
channex.getConnection(options?) |
GET /api/v1/channex/connection |
{ data: { connected: false } } or connected status with connected_at and property_count |
channex.connect({ api_key }, options?) |
PUT /api/v1/channex/connection |
{ data: { connected: true, connected_at, property_count } } |
channex.listProperties(options?) |
GET /api/v1/channex/properties |
{ data: [{ id, title }] } |
Connect through dashboard onboarding, or from your own secure credential input:
// channexKey comes from a secure input; do not log it or put it in a URL.await guestcraft.channex.connect({ api_key: channexKey });Connection validates a Channex key with the workspace’s Channex environment (staging for a sandbox workspace, production otherwise) and saves an encrypted credential for that workspace. The key is never returned. Connection status and count are a saved snapshot; listing fetches Channex again. Listing does not import or map properties. Channex IDs are distinct from local GuestCraft prop_… IDs. Booking ingestion and guarded ARI have separate workflows; connecting alone does not enable them. See the developer import and ARI contracts.
Errors, cancellation and retries
Section titled “Errors, cancellation and retries”import { GuestCraftError } from "@guestcraft/sdk";
const controller = new AbortController();try { await guestcraft.channex.listProperties({ signal: controller.signal });} catch (error) { if (error instanceof GuestCraftError) { // Branch on code; title and detail can be translated. const code = error.code; const requestId = error.requestId; const fields = error.problem?.errors; } else { throw error; // Cancellation preserves the caller’s AbortSignal reason. }}kind: "http":status,requestId, and a validated RFC 9457problemwhen provided.codeis the problem’s stable code. Non-JSON proxy errors still retain HTTP status and the request ID header.kind: "network": fetch or response-body reading failed. The SDK omits untrusted transport causes, request bodies and credentials from its errors.kind: "invalid_response": a successful response failed JSON or structural schema validation. Date-time formats remain strings and are checked by the generated Zod schema.
The SDK logs nothing. Error messages omit response detail; problem deliberately exposes the server’s public problem details. Treat those as untrusted text in your UI and avoid logging credentials yourself.
There are no automatic retries, including on reads. Credential writes are never replayed. A failed or cancelled write may already have reached the server: inspect connection status before deciding to reconnect. Pass an AbortSignal for cancellation or a caller-owned deadline, such as AbortSignal.timeout(25_000).
Contract and validation
Section titled “Contract and validation”pnpm --filter @guestcraft/api openapi # Backend owner exports the API contract.pnpm --filter @guestcraft/sdk generatepnpm --filter @guestcraft/sdk generate:checkpnpm --filter @guestcraft/sdk typecheckpnpm --filter @guestcraft/sdk testPinned Hey API in tooling/codegen generates types, operations, Zod response schemas and TanStack Query factories from openapi/v1.json. The local script splits them into one module per tag, then compiles and packages them; it reads only each operation’s tag from the contract. SDK output is JavaScript plus declarations, checked by strict handwritten TypeScript consumers (ADR 0022 and its 2026-10-30 amendment). The drift test checks the generated files byte for byte and verifies that all current operations are covered. Transport tests exercise tenant headers, session options, API/proxy errors, response validation, cancellation and credential-write failures.
See the instance’s /docs hub, /api/v1/docs reference and /api/v1/openapi.json, or the repository’s developer guides.
Local settings use { name, time_zone, currency }. Creating and updating need properties.configure (built-in: administrators); reading needs properties.read. See property setup. No automatic retries are added for property writes.
Front desk API
Section titled “Front desk API”roomTypes, units, guests, reservations, reservationRooms, availability,
calendar and frontDesk expose the generated operations through the caller transport.
Use typed Hey API options (path, body, query, headers, signal); reservation
and guest edits require headers: { "if-match": '"1"' }. Responses validate frozen decimal
prices and totals. No retries are added. See the complete workflow and method
examples. reservationRooms.move and
reservationRooms.split (ADR 0042) take the same if-match header and an optional
query: { validate_only: "true" } preview. reservationRooms.resize changes stay dates through
the existing date-change rules. setDoNotMove persists the permissioned stay flag; protected
changes require confirm_do_not_move: true. A successful planning write returns an optional
planning_change_id; pass that receipt to reservationRooms.undo with the exact ETag returned
by the original write. Undo is actor-bound and never retries a version conflict.
Team management
Section titled “Team management”client.team exposes the member, invitation, custom-role, property-access and sign-in-lock
operations listed in the facade above, with generated options (body, query, signal).
Configure workspaceId on the client to send GuestCraft-Workspace-Id; deprecated
workspace_id aliases must match it. Legacy role/removal writes also require If-Match. Writes require the instance Origin header with the session
cookie; browsers send it automatically. Invitation links are secrets: do not log/cache
the mutation result. Creation/rotation returns a link regardless of account existence.
The link expires in 48 hours. See ADR 0030 for workflow and pagination limits.
The role catalog has 69 capabilities, custom-role resources and a member-access sub-resource with strong preconditions. Use the role workflow for permission codes and selected-property rules; delegation is bounded by the actor’s live permissions and property context fails closed.
ratePlans.{list,create,update,archive}, rates.{get,apply,restore} and quotes.create
expose all eight generated rate operations through the same transport. Setup mutations
use plan If-Match; nightly mutations use calendar If-Match. Quotes hold no inventory.
See the rates contract for examples, booking input changes
and planned capabilities.
Channex
Section titled “Channex”The handwritten channex group wraps the connection, booking-import mapping, sync health,
drain, webhook registration and recovery operations, the property’s open alerts and their openings and resolutions
(alertTransitions, each to be shown once by its event id),
channex.ari the ARI health, plan, mapping, repair and resync operations, and
channex.bookingHistory the preview, import and list of a property’s existing Channex bookings; generated React factories expose the same contracts
without retaining credential data. channex.patchConnection takes body.account_mode and the
connection version as a quoted headers["if-match"]; connection reads include the mode and
version. Shared listProperties returns mapped properties; link an explicit property ID with
room_types: {} before room discovery.
channex.candidate({path:{id}}) reads the candidate title/ID/connection version; channex.map accepts the confirmed ID/version for an initial shared link. channex.unlink({path:{property_id},headers:{"if-match":connectionEtag}}) deletes the mapping workflow. Unlink preserves imported reservations; failed webhook cleanup is visible in sync().data.pending_unlinks and can be retried. See the booking import and ARI publication contracts.
Property reports
Section titled “Property reports”reports.history({ path: { property_id } }) discovers the captured history boundary.
reports.performance({ path: { property_id }, query: { from, to, group_by } }) returns
performance with historical capacity (definition version 2, API revision 5.0.0). Older ranges
use labelled baseline capacity; pre_history_nights counts affected stay dates. reports.pickup adds two
as-of dates and compares booked rooms/revenue for each stay date. reports.cash and
reports.revenue group the append-only ledger by service date. Each has a matching
Csv method returning text (pass parseAs: "text"). Generated operations and React query
factories expose the same contracts; the MIT client imports no core code. See the
report contract for exact inputs and coverage limits.
reports.consolidated({ query: { from, to, reporting_currency } }) sums the workspace’s
authorized properties per currency; with reporting_currency each currency’s subtotal is
converted once with the organization’s stored rates, listed next to the converted amounts, and a
missing or stale rate answers exchange_rate.missing. reports.organizationConsolidated adds
include_sandbox and covers the organization’s active workspaces for its owners and
administrators in a browser session. exchangeRates.list reads the stored rates;
exchangeRates.create and exchangeRates.delete need a browser session.
Housekeeping and maintenance
Section titled “Housekeeping and maintenance”housekeepingSettings, housekeepingDays, housekeepingTasks and housekeepingAssignees cover a
property’s stayover and inspection settings, the on-demand generation of a day’s tasks, manual
tasks, assignment and the task lifecycle, one by one or up to 100 at once (the bulk operations take
each task’s version in the body). maintenanceTickets reports and edits faults, sets their status
with a housekeeping follow-up, reads their activity and manages their photos;
maintenanceTickets.photos.create takes a JPEG or PNG in base64 and maintenanceTickets.photos.get
answers the bytes as a Blob (the facade reads it as one; with the generated operation, pass
parseAs: "blob"). Creations accept a client id, so that a
repeated request answers the same task, ticket or photo. housekeepingRosters and
housekeepingCredits hold who works on a day, with how many credits of work, and how many credits
each task takes; housekeepingAssignmentPlans.get previews a day’s automatic assignment and
housekeepingAssignmentRuns.create applies it with the preview’s basis, which undo reverses
exactly. housekeepingChecklists manages checklist templates, copied onto each new task, and
housekeepingTaskChecks answers a task’s items with the task’s version. See ADR 0046.
Guest data rights
Section titled “Guest data rights”guestDataRetention reads and replaces the workspace’s retention periods, with the country pack
defaults they replace. guestLegalHolds places and releases a guest’s legal hold, which blocks
erasure and retention. guestErasure.create anonymizes a guest and its merged identities, and
guestDataExports.create queues an access export; guestDataRequests lists and reads the
requests, and guestDataRequests.download answers a ready export’s ZIP archive once, as a Blob
(the facade reads it as one; with the generated operation, pass parseAs: "blob"). Every
operation needs a browser session. See ADR 0052.
Folios
Section titled “Folios”Reservation folios and manual settlement are documented in the folio workflow and ADR 0029. The ledger, frozen room charges, property extras, manual payments/refunds, a check-out that settles only at the last room and never holds a credit (an owed balance needs settlement or an audited administrator approval), refunds due, net and full-ledger views, a non-fiscal statement, split folios with payers, routes and transfers, per-room settlement and bulk re-rating are implemented. Taxes need a country module on the TaxCalculator port (with a re-rate operation); fiscal documents and online providers remain planned.
Booking policies and no-shows
Section titled “Booking policies and no-shows”Property policies, immutable sold conditions, simulated cancellation fees, explicit no-show/recovery, guarantee attestations and prepayment tracking are documented in the policy workflow and ADR 0032, with permissions, deadlines, migration semantics, SDK operations and planned OTA reporting/provider capabilities.
Night audit
Section titled “Night audit”nightAudits covers property business dates, automatic and manual night audits; see the workflow contract and ADR 0043. Local-time scheduling, configured no-shows and fee-free expired options use that same durable path.
Audit automation and cash drawers
Section titled “Audit automation and cash drawers”Night audit settings, configured no-shows and expired tentative options share the durable property-date audit workflow. Cash drawer shifts provide currency-aware opening float, server-linked cash activity and versioned counted closure. See the night audit contract and cash shift contract for public operations, permissions, recovery, persistence and the Vercel daily-trigger cadence limit.
Receivables
Section titled “Receivables”Counterparties are an organization directory reached from the organization’s active workspaces;
open items, their append-only entries (debits, allocations, write-offs, releases, cancellations)
and receipts belong to one workspace. Closing a folio linked to a counterparty books one
receivable_transfer movement and opens its item; reopening reverses it exactly. OTA-collected
bookings open expected payouts from the revision’s facts. See the
receivables guide and ADR 0051.
Guest identity
Section titled “Guest identity”guests.duplicates and guestDuplicates page reasoned duplicate pairs. dismissDuplicate
remembers an operator decision. merge takes the survivor If-Match, absorbed version and
per-field sources; reassignOwnership changes owning property separately. consents and
recordConsent preserve independent append-only evidence histories. Merge never combines
consents. See the guest workflow and ADR 0045 for
permissions, tombstones and every current guest-reference policy.
contactNotices returns newest-first OTA snapshots with only eligible fields in contact.
Overlay those fields on the current guest for ordinary update, retaining its notes.
dismissContactNotice keeps one field through a versioned resource:
path: { id, reservation_id, field }, headers: { "if-match": '"3"' }, and
body: { revision_id, expected_value }. Its returned guest carries the current version.
Dismissed values remain suppressed for that guest and field across later snapshots; other
fields remain independent. Refetch the guest and notices after a 412 guest version conflict
or 409 guest.contact_notice_changed. Do not automatically retry these writes.
Organizations and workspaces
Section titled “Organizations and workspaces”API 6.4.0 adds guestcraft.organizations.list/create/audit and guestcraft.workspaces.create/get/update/setArchival, also available from generated operations/organizations and operations/workspaces with React Query factories. These administration methods require a browser session; writes require the instance Origin. Item reads return a strong ETag (also etag in the body), passed as headers: { "if-match": workspace.etag } on updates and archival. Browser workspaces.list() includes reachable organizations and archived recovery entries.
API 6.11.0 pages workspace Activity like organization Activity: pass the previous page’s
next_keyset_cursor to team.audit as keyset_cursor; has_more says whether another page
follows. The numeric cursor and next_cursor of 6.6.0 still work, deprecated; never send both. team.auditExportCsv/auditExportJson and organizations.auditExportCsv/auditExportJson
download the entries that match the same filters, with from and until required at most 366
days apart. Call the CSV methods with parseAs: "text". More than 10,000 entries or 20 MiB
answer activity.export_too_large with max_entries and max_bytes; narrow the range. Each export is audited.