@guestcraft/api
Questi contenuti non sono ancora disponibili nella tua lingua.
GuestCraft’s private, AGPL-3.0-only HTTP boundary: Hono routes, request/response schemas,
authentication middleware, RFC 9457 problems and an OpenAPI 3.1 document. The package reads
no environment and opens no connections. server/http wires the
adapters; apps/web mounts the resulting app under /api. See
ADR 0016 for HTTP conventions.
Public API
Section titled “Public API”The package entry point exports:
| Export | Purpose |
|---|---|
createApi(dependencies, options?) |
Build an app from ApiDependencies; call app.request() in tests or mount its handler in a runtime. |
ApiDependencies, ApiOptions, ApiEnv and gateway types |
Describe injected use cases, authentication, request policy, discovery/device services, clock, randomness and logging. |
openApiDocument() |
Generate the contract offline: it registers routes with refusing dependencies, skips createApi’s middleware, runs no use case and needs no database or credentials. |
API_REVISION, SERVER_URL |
Contract revision and the composed /api/v1 base path. |
PROBLEM_STATUS, PROBLEM_MEMBERS, FIELD_CODES, problemType and related types/constants |
Stable public problem codes, status mapping and allowed extension members. |
REQUEST_ID_HEADER, DEFAULT_MAX_BODY_BYTES, SCALAR_SCRIPT, ReferenceScript |
Request-id, body-limit and self-hosted reference configuration. |
This composition helper takes already-wired services and passes every dependency explicitly.
ApiDependencies requires the tenancy, team, account-security, booking-import, history-import, booking-report, alert, messaging, ARI, enterprise-identity and guest data rights adapters along with
the other use cases. Supply the runtime’s cookie-origin policy; it is required even when an
embedding uses another authentication mechanism.
import { type ApiDependencies, createApi } from "@guestcraft/api";
export function buildApi(services: ApiDependencies) { return createApi({ useCases: services.useCases, properties: services.properties, reservations: services.reservations, folios: services.folios, cashShifts: services.cashShifts, rates: services.rates, reports: services.reports, receivables: services.receivables, consolidatedReports: services.consolidatedReports, exchangeRates: services.exchangeRates, blocks: services.blocks, housekeeping: services.housekeeping, housekeepingPlanning: services.housekeepingPlanning, housekeepingChecklists: services.housekeepingChecklists, maintenance: services.maintenance, nightAudits: services.nightAudits, tenancy: services.tenancy, team: services.team, security: services.security, enterpriseIdentity: services.enterpriseIdentity, channex: services.channex, bookingImport: services.bookingImport, messages: services.messages, payments: services.payments, privacy: services.privacy, ari: services.ari, channexAlerts: services.channexAlerts, bookingHistory: services.bookingHistory, bookingReports: services.bookingReports, apiKeys: services.apiKeys, organizationTokens: services.organizationTokens, clientSessions: services.clientSessions, oauthApplications: services.oauthApplications, webhooks: services.webhooks, eventCatalog: services.eventCatalog, events: services.events, principals: services.principals, auth: services.auth, cookieRequestAllowed: services.cookieRequestAllowed, actorNames: services.actorNames, instance: services.instance, devices: services.devices, accounts: services.accounts, clock: services.clock, random: services.random, logger: services.logger, });}Endpoints
Section titled “Endpoints”The generated API reference and
committed OpenAPI document list every method, path, request,
response, permission and security scheme. The resource groups below use those tag names;
paths in the document are relative to /api/v1. The table uses the app’s own paths
(/v1/...); apps/web serves them under /api, and the OpenAPI document lists them
relative to its /api/v1 server.
GET /v1/openapi.json serves the contract with a five-minute cache. GET /v1/docs serves
Scalar using the runtime’s local bundle, a script nonce and a restrictive Content Security
Policy. It gives Scalar the copy of the committed document that the web build has processed
for that bundle, so that Scalar renders it without processing it first (ADR 0016); its download
links to openapi.json. ApiOptions.reference can select a different script, which then
processes the document itself, or disable the page; the JSON contract remains available. /auth/* delegates to the injected auth handler in its own format.
Request and permission boundaries
Section titled “Request and permission boundaries”- Browser sessions, device bearer tokens and the integrations’ credentials (workspace API keys,
organization tokens and client sessions) are separate credentials. Each operation declares
which it accepts. Administration of tenancy, teams, Channex, API keys, organization tokens and webhooks
requires a browser session; only an API key or an organization token mints client sessions.
Invitation acceptance uses a single-use invitation token. A key or token is constrained by its
creator’s live permissions, and a client session by its minting credential’s; the
key-management/delivery rules are in the linked integration contract. A client session limited
to one reservation, one guest or one property’s public booking reaches only the operations that
routes/client-session-scope.tslists for its scope, with path parameters naming its own objects;authenticateanswers any otherauth.forbiddenand reports it for the audit. crossOriginanswers the CORS preflight of every/v1operation for any origin, never with credentials, and lets a page read only the answers of requests with a client session’s token (ADR 0047).GuestCraft-Workspace-Idselects accessible workspace context; it grants no permission. Property operations enforce their concrete property scope. Resource writes that require strongETag/If-Matchdocument the precondition in OpenAPI.- The cookie request policy runs before body parsing and authentication. Refusals return
request.untrusted_origin(403). Raw encoded paths pass through the middleware; control characters are rejected. - Every response has a validated or generated
Request-Id. Access logs exclude query strings, cap the encoded path at 2 KB and identify API-key callers by key ID, never the secret. - API responses default to
Cache-Control: no-storeandX-Content-Type-Options: nosniff; a maintenance photo is answered as its bytes withCache-Control: private, max-age=300, and a guest data export as its ZIP archive, an attachment that staysno-store. The default body limit is 1 MiB, 4 MiB for rates restore and 3 MiB for message attachments and maintenance photo uploads;maxBodyBytesoverrides all of them. Limits also apply withoutContent-Length. - Body operations expect supported JSON media types. Send
Content-Type: application/json; an unsupported type is 415. An empty JSON body isrequest.malformed(400). Omitting a required body isrequest.invalid(400), including schemas whose fields are all optional: send{}for those operations. Optional bodies may be omitted.
Problems
Section titled “Problems”API errors use application/problem+json (RFC 9457) with stable type, code and status,
a request_id, and only the extension members allowed by PROBLEM_MEMBERS. Clients branch
on codes rather than parsing detail. Auth handler responses retain their own format.
Problem titles/details come from the English catalog. Content-Language identifies the
chosen language and Vary: Accept-Language marks negotiation; the application ships English
only. Unexpected failures answer server.internal (500) without internal error details.
Retryable infrastructure failures answer server.unavailable (503), with Retry-After when
known. Admission refusals of API keys and device tokens answer auth.rate_limited (429) with
Retry-After.
A new problem needs a status in PROBLEM_STATUS, an explicit member list in PROBLEM_MEMBERS
(even when empty), and described title/detail entries in packages/i18n/catalogs/en.json. Run
pnpm i18n:catalogs; tests check code coverage by the problem catalog and allowed placeholders.
OpenAPI and tests
Section titled “OpenAPI and tests”pnpm --filter @guestcraft/api testpnpm --filter @guestcraft/api typecheckpnpm --filter @guestcraft/api openapipnpm --filter @guestcraft/scripts test:integration test/integration/readme-examples.integration.test.tsTests exercise app.request(), middleware and route contracts using injected services and
in-memory fakes. The OpenAPI test compares the offline export with the committed artifact;
exporting needs no database or credentials. The documentation example check compiles the
actual README fences with the package’s strict TypeScript settings. Persistence and isolation
are verified separately by database/server integration tests.
Public contract changes flow from these route schemas to OpenAPI, then the generated SDK and React factories; do not edit generated outputs by hand. Contract decisions are recorded in the ADRs; each workflow guide names the revision that introduced it.
Guest duplicate review, two-version merge, consent evidence and owning-property
reassignment use the guests tag. See the guest workflow
for permission scope, survivor pointers and preserved history.
Guest OTA contact review returns only eligible fields, newest snapshot first. Operators can
apply selected fields through ordinary guest replacement or keep a field with
PUT /guests/{id}/contact_notice_dismissals/{reservation_id}/{field} using the guest ETag,
snapshot revision and expected value. Dismissed values remain suppressed for that identity
and field across later snapshots; the field decision is audited without contact values.
See the API changelog for problems and recovery.
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 request-woken Vercel scheduling with the daily cron safety net.