Reservations
Build booking, financial-line, adjustment, and mapping workflows
Mental Model
A reservation connects booking identity and stay context to accounts receivable, revenue, fees, deposits, adjustments, and owner statements. The API constructs accounting output; the client renders it.
Resources and Lifecycle
Reservations contain listing, guest, stay dates, source identity, booking status, payment lines, adjustments, and calculated financial output.
Writes accept the booking statuses booked, cancelled, canceled,
inquiry, and inactive; both cancellation spellings are equivalent on
write. Reads always return booked, canceled, or inactive. Booking
status and generalLedgerStatus=active|inactive are separate. The normal
reservation update (PUT /reservations/{id})
changes booking status; the dedicated status update
(PUT /reservations/{id}/status)
activates or inactivates reservation journal entries.
Read Model
Render returned financial lines, resolved accounts, posting dates, totals, paid state, locks, and issues. Do not recreate cancellation, deferred revenue, fee calculation, or journal generation in the client.
Reservation list and detail share one materialized issues snapshot.
Detail reads compute issues from live data and write them back to the
snapshot. List reads serve that stored snapshot: issuesComputedAt is the
completion time of its latest durable evaluation (null before the first one),
and issuesStale=true means a committed change has a newer revision. The
returned issues are the previous snapshot, or an empty pending snapshot for a
new reservation. Keep rendering the snapshot, show pending freshness where
relevant, and re-read while it is stale. Do not merge it with client-side
issue calculations.
Committed reservation and account mutations mark affected snapshots and wake a serialized regional evaluator. Required effects repeat invalidation after they commit. A minute recovery task seeds and reconciles bounded batches and keeps draining while durable work remains. An immediate list re-read can briefly still return the previous snapshot. Issue computation stays out of list reads, so they do not expand journal entries and accounts for each reservation.
Financial lines omit the generated A/R mirror leg of a reservation adjustment, including each daily leg of a pro-rata adjustment. The adjustment itself and genuine deposit or other A/R-account activity remain visible. Do not recreate the accounting mirror in the client. The mirror remains an active journal entry because it balances an adjustment posted independently from the reservation A/R date. This omission applies only to the reservation financial folio; general-ledger and report surfaces must apply their own documented accounting rules.
For a booked, accounting-active reservation whose posting date is before the
accounting start, a refresh persists its base journal entries as inactive with
inactiveReason=generalLedgerStartAt. Until that refresh, detail reads can
still derive the mapped, non-excluded guest charge without journal entries.
In either shape, reservation lines have status=inactive, and issues
contains priorToStartDate with context.startDate after issue evaluation.
Render that warning and the inactive line state instead of treating booking
status=booked as proof that the reservation posts to accounting.
A reservation-level generalLedgerPostingAt supersedes its default
recognition date when evaluating the PMS accounting window and generating base
journals. Refreshing with an open date at or after the accounting start moves
the base entries to that date, activates them, and recalculates journal-backed
payment and financial totals. A manually
generalLedgerStatus=inactive reservation remains a separate explicit
exclusion and keeps its zero expected-payment behavior.
Use returned source and connection state to decide which synced fields the user can edit.
For reservation preflight, pass comma-separated reservation IDs through the
ids filter. An inclusive ID request is a targeted lookup: every accessible
matching reservation is returned even when normal browsing hides it because
of booking, connection, source, or inactive-ownership accounting state. The
pagination total reflects the targeted matches. Prefixing the filter with !
keeps normal browse rules and excludes the listed reservation IDs.
Payment Status
Use the returned payment.expected, payment.received, and payment.status
on reservation detail, list, and owner-statement rows. These surfaces apply the
same journal-backed payment rule.
A true co-host payout leaves only the manager share collectible. A manual deposit reclass is different when one transaction contains both a co-host offset and an accounts-receivable leg equal to the full guest charge. That transaction is a bookkeeping reclass of the full guest payment, so expected and received both remain the full guest charge and the reservation is paid. Do not infer either case from the presence of a co-host line alone. Archiving or restoring a deposit recalculates linked reservation payment status after its journal entries change status.
The reservation-list paidStatus filter accepts unpaid, underpaid,
overpaid, and paid. Prefix one of those values with ! to exclude it, such
as paidStatus=!paid. Any other value is rejected as an invalid query; clients
must omit an unset filter instead of sending strings such as undefined or
null.
Decision Table
| Context change | Recalculate or validate |
|---|---|
Booking status (booked, cancelled/canceled, inquiry, inactive) | Active financials, adjustments, and journal consequences |
generalLedgerStatus | Journal activity without changing booking status |
| Listing or stay dates | Ownership, recognition, fees, and posting dates |
| Channel or line type | Line mapping and recognition override |
| Adjustment party | Owner-period eligibility and locks |
| Recurring fee adjustment | Fee posting accounts and metadata |
generalLedgerPostingAt | Reservation PMS boundary and journal date; adjustment journal date |
Adjustments and Posting Dates
An adjustment can specify a line type or account, amount, description,
owners|manager party, optional recurring fee, and optional
generalLedgerPostingAt.
Recurring-fee adjustments use their own balanced journal group. If the active fee references an archived posting account, create or update returns a user error before persistence; it does not inactivate the reservation's revenue, deferred-revenue, or A/R rows. Repoint or deactivate the fee before retrying. The final materialized journal also validates derived deferred-revenue postings. An inactive derived posting account taints the refresh and preserves the existing journal instead of writing a partial replacement.
When creating an adjustment for an active booking, provide either type or
recurringFeeId; selecting only accountId does not classify the line.
Canceled bookings may preserve an unclassified adjustment because their
financial treatment is derived from the cancellation state.
Owner adjustments participate in ownership and statement locks, while manager adjustments normally avoid owner-period locking. A separate open posting date is required when a new owner adjustment would otherwise reuse a locked reservation A/R date. Use returned date previews rather than assuming stay, cancellation, or A/R date.
For adjustment creation,
either provide a concrete generalLedgerPostingAt or set onLocked=nextOpen.
The default onLocked=error rejects a locked natural posting date. nextOpen
keeps the natural date when it is open; otherwise it resolves and persists the
earliest date allowed by books closing and statement-period locks. It does not
bypass journal locking, and it cannot be combined with an explicit posting
date.
The dry-run response includes the resolved concrete
adjustments[].generalLedgerPostingAt without saving the adjustment. The
policy applies only when creating an adjustment; adjustment updates continue
to require an explicit posting date when moving the line.
Line Mappings and Revenue
Payment-line mappings translate PMS or channel line types into accounts and can override recognition. Recognition can use check-in, check-out, booked-at, or pro-rata dates. Pro-rata can cross months and ownership periods.
After a PMS is connected, default mappings are bootstrapped only for line types from that PMS which have occurred in reservations for at least two teams. Mappings from unrelated providers, generic template mappings, and line types seen in only one team are not bootstrapped.
For a pro-rata owner adjustment, each recognized daily amount and its matching accounts-receivable amount post on the same date. This includes uneven cent splits; callers do not need to force a separate posting date to keep the generated journal balanced.
When a reservation crosses the accounting start date but its reservation-level A/R remains excluded before that date, a line recognized after the start is bridged from opening equity. For a pro-rata line, only the post-start portion uses that bridge; earlier portions remain inactive, and Deferred Revenues clears when the post-start revenue is recognized.
For a reservation sourced from a child in a listing group, journal entries keep the child listing as their source and resolve ownership through the group parent. This includes deferred-revenue legs and rebalances. A missing-ownership issue is returned only when neither the stored period nor an effective parent period covers the posting date.
To deliberately exclude a line type from journal posting, create or update its
mapping with accountId: null. Use a channel mapping with accountId: null to
exclude only that booking channel. Mapping status=excluded and reservation
line isExcluded=true are derived read-only results; omitting a line from a
reservation payload is not the exclusion control.
Editability
Reservation changes can be blocked by books closing, statement periods, statement attachments, inactive ownership, or source ownership of synced fields. Use the most specific entity or adjustment lock.
An adjustment with an independent open posting date can remain editable while the reservation's owner-side A/R posting is locked. Updating or deleting an adjustment is blocked when its entries are statement-attached or in closed books.
Preview and Preflight
Create, update, batch, booking/accounting status, removal, and adjustment
mutations expose dry run where the generated OpenAPI declares the dryRun
parameter. Journal regeneration and line-mapping changes intentionally skip
generic dry run because they require consequence-oriented results.
Recalculate financial previews when channel, line type, mapping, stay dates, recognition settings, party, fee, or posting date changes.
Mutation Recipe
- Load the reservation, financial output, mappings, and returned locks.
- Apply source-editability and deterministic status rules.
- Recalculate affected account and posting-date previews.
- Add an independent adjustment posting date when required.
- Dry-run the complete reservation or adjustment payload.
- Render structured lock and mapping issues.
- Confirm and apply.
- Re-read the reservation and render returned accounting output; continue
polling issue state while
issuesStale=true.
Failure and Recovery
A missing mapping means the user needs to configure the exact source line type. Locked owner history calls for showing the relevant period, statement, or first open date rather than silently changing the adjustment to manager. After a source sync or concurrent mutation, reload before retrying. An inactive fee posting account requires fee or account remediation; retrying the adjustment without changing that configuration cannot succeed.
Common Recipes
Cancel a paid reservation
Update booking status through the reservation mutation, then render returned issues and adjustments. Do not locally synthesize refund journals.
PUT /reservations/{reservationId}?dryRun=true
Content-Type: application/json
{
"status": "canceled",
"cancelledAt": "2026-07-19"
}Post a late owner adjustment
Choose owners, create with onLocked=nextOpen&dryRun=true, inspect the
returned posting date and financial lines, then repeat without dryRun to
apply. Use an explicit generalLedgerPostingAt instead when the posting date
is a user decision.
POST /reservations/{reservationId}/adjustments?onLocked=nextOpen&dryRun=true
Content-Type: application/json
{
"type": "lateFee",
"amount": 2500,
"description": "Late owner charge",
"party": "owners"
}Webhook Reconciliation
reservation.changed is emitted after a committed public aggregate change and
after required journal or payment effects finish. No-ops, dry runs, rollbacks,
and journal-only work that leaves the public response unchanged do not emit.
The compact body contains no guest, stay, amount, payment, source, or line data;
compare resourceVersion and fetch href.
API Reference
- Get reservation detail —
GET /reservations/{id} - List reservations —
GET /reservations - Update a reservation —
PUT /reservations/{id} PUT /reservations/{id}/status— Update general ledger statusPOST /reservations/{reservationId}/adjustments— ContractPUT /reservations/{reservationId}/adjustments/{id}— ContractPUT /reservations/line-mappings/{type}— Contract
