Kaizen
Browse modulesBookingsbookings/serverClasses

Class: WaitlistService

Defined in: server/bookings/waitlist-service.ts:101

BookingService + the §7.1 transfer family (transfer / reschedule / transferMany). Shipped as a subclass so the spec's "BookingService gains transfer(...)" surface (§11) holds — the factory exposes ONE service that is-a BookingService — while the transfer machinery lives in its own module.

Extends

Constructors

Constructor

new WaitlistService(deps: BookingFeatureDeps): WaitlistService;

Defined in: server/bookings/booking-service.ts:135

Parameters

ParameterType
depsBookingFeatureDeps

Returns

WaitlistService

Inherited from

TransferService.constructor

Properties

deps

protected readonly deps: BookingFeatureDeps;

Defined in: server/bookings/booking-service.ts:135

Inherited from

TransferService.deps

Methods

acceptOffer()

acceptOffer(bookingId: string, opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/waitlist-service.ts:773

The offered party owning bookingId accepts (§6.10) — the whole party batch-confirms all-or-none. A party of one is the batch of one. Delegates to the batch-confirm primitive; capacity was already earmarked by the offered state, so the self-excluding re-check passes.

Parameters

ParameterTypeDescription
bookingIdstring-
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; }-
opts.actorIdstring-
opts.allowLateCancel?booleanAdmin late-cancel override (spec §7): when true, cancel skips the rules-engine cancellation policy guard and records a cancelled_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook (§6.6). The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than cancel.
opts.allowOutsideHours?booleanAdmin outside-hours override (spec §6.9): when true, claiming transitions skip the window-containment check and record an hours_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook. The CONSUMER owns the permission decision.
opts.allowOverbook?booleanAdmin overbook override (spec §6.6): when true, claiming transitions skip the capacity/overlap check and record an overbooked_by_admin timeline event instead. The CONSUMER owns the permission decision — taproot is auth-agnostic and only honors + records the flag.
opts.allowTransfer?booleanAdmin transfer override (spec §7.1): when true, the transfer/reschedule family skips the rules-engine policy guard for the transfer event and records a transfer_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowLateCancel/allowOverbook. The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than transfer.
opts.expectedVersion?number-
opts.reason?string-

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>


acceptOfferParty()

acceptOfferParty(bookingIds: readonly string[], opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}[]>;

Defined in: server/bookings/waitlist-service.ts:920

Batch accept for a whole party (§6.10) — all-or-none. Every member runs through applyClaimTransitions (savepoint, capacity re-check with self-exclusion, structural guard), and each member's active offer row flips to confirmed + a waitlist_offer_accepted event is appended, all in one transaction.

APPROVAL ROUTING (§7): a requiresApproval type must not reach confirmed without a reviewer, and the waitlist is a second road there — one whose offer is sent automatically by the cascade, with no human in the loop. So for those types the party accepts into pending (acceptOfferForReview) and waits for the normal approve/reject edges; approval_requested is appended alongside the acceptance. Capacity is unaffected either way — offered and pending both block (§6.6) — so the party holds its slot while it waits, and applyClaimTransitions stamps pendingExpiresAt from the pinned pendingTtlSeconds like any other entry into pending.

The whole party takes ONE road: if ANY member's pinned policy requires approval the entire party is routed for review. A party is a unit (§6.10), and erring toward review can only ever add scrutiny, never skip it.

Parameters

ParameterTypeDescription
bookingIdsreadonly string[]-
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; }-
opts.actorIdstring-
opts.allowLateCancel?booleanAdmin late-cancel override (spec §7): when true, cancel skips the rules-engine cancellation policy guard and records a cancelled_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook (§6.6). The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than cancel.
opts.allowOutsideHours?booleanAdmin outside-hours override (spec §6.9): when true, claiming transitions skip the window-containment check and record an hours_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook. The CONSUMER owns the permission decision.
opts.allowOverbook?booleanAdmin overbook override (spec §6.6): when true, claiming transitions skip the capacity/overlap check and record an overbooked_by_admin timeline event instead. The CONSUMER owns the permission decision — taproot is auth-agnostic and only honors + records the flag.
opts.allowTransfer?booleanAdmin transfer override (spec §7.1): when true, the transfer/reschedule family skips the rules-engine policy guard for the transfer event and records a transfer_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowLateCancel/allowOverbook. The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than transfer.
opts.expectedVersion?number-
opts.reason?string-

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }[]>


applyClaimTransitions()

protected applyClaimTransitions(
   members: readonly ClaimMember[], 
   event: string, 
   input: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
}
): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}[]>;

Defined in: server/bookings/booking-service.ts:709

The batch CLAIM core (§6.6/§6.10) — every capacity-acquiring transition runs through here; a single claim is the batch of one.

One repos.transaction (15s budget) wrapping ONE explicit savepoint, so all-or-none survives a consumer's ambient outer transaction (§6.6 joined-tx contract). Inside:

  1. hydrate + policy + STRUCTURAL evaluation for EVERY member — no writes;
  2. the §6.6 batch capacity check over the union overlap set (real row locks, sorted-id order) — or, under allowOverbook, skip it and append one overbooked_by_admin event per schedule-bearing member;
  3. only then apply every member's transition: version-CAS + timeline + is_blocking maintenance + outbox rows.

Any member failure throws before/instead of step 3 and rolls back to the savepoint — none commit. A batch of one unwraps to the inner error.

Parameters

ParameterTypeDescription
membersreadonly ClaimMember[]-
eventstring-
input{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; }-
input.actorIdstring-
input.allowLateCancel?booleanAdmin late-cancel override (spec §7): when true, cancel skips the rules-engine cancellation policy guard and records a cancelled_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook (§6.6). The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than cancel.
input.allowOutsideHours?booleanAdmin outside-hours override (spec §6.9): when true, claiming transitions skip the window-containment check and record an hours_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook. The CONSUMER owns the permission decision.
input.allowOverbook?booleanAdmin overbook override (spec §6.6): when true, claiming transitions skip the capacity/overlap check and record an overbooked_by_admin timeline event instead. The CONSUMER owns the permission decision — taproot is auth-agnostic and only honors + records the flag.
input.allowTransfer?booleanAdmin transfer override (spec §7.1): when true, the transfer/reschedule family skips the rules-engine policy guard for the transfer event and records a transfer_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowLateCancel/allowOverbook. The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than transfer.
input.expectedVersion?number-
input.reason?string-

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }[]>

Inherited from

TransferService.applyClaimTransitions


applyTransition()

protected applyTransition(
   bookingId: string, 
   event: string, 
   opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
}, 
   extras?: TransitionExtras
): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/booking-service.ts:597

Parameters

ParameterTypeDescription
bookingIdstring-
eventstring-
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; }-
opts.actorIdstring-
opts.allowLateCancel?booleanAdmin late-cancel override (spec §7): when true, cancel skips the rules-engine cancellation policy guard and records a cancelled_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook (§6.6). The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than cancel.
opts.allowOutsideHours?booleanAdmin outside-hours override (spec §6.9): when true, claiming transitions skip the window-containment check and record an hours_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook. The CONSUMER owns the permission decision.
opts.allowOverbook?booleanAdmin overbook override (spec §6.6): when true, claiming transitions skip the capacity/overlap check and record an overbooked_by_admin timeline event instead. The CONSUMER owns the permission decision — taproot is auth-agnostic and only honors + records the flag.
opts.allowTransfer?booleanAdmin transfer override (spec §7.1): when true, the transfer/reschedule family skips the rules-engine policy guard for the transfer event and records a transfer_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowLateCancel/allowOverbook. The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than transfer.
opts.expectedVersion?number-
opts.reason?string-
extras?TransitionExtras-

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>

Inherited from

TransferService.applyTransition


approve()

approve(bookingId: string, opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/booking-service.ts:385

pending→confirmed — deliberately NON-claiming, unlike confirm-from-held.

Capacity was acquired at submit (the claim path), and pending blocks UNCONDITIONALLY in the §6.6 truth table — it carries no query-side expiry predicate, so its occupancy can never silently lapse between submit and approve. (pending→expired is a sweeper-driven version-CAS transition; racing it makes this approve throw StaleBookingError rather than double-book.) Contrast held, whose occupancy evaporates the moment holdExpiresAt passes: confirming an expired-but-unswept hold RE-ACQUIRES capacity, so confirm must re-check under the overlap-set lock. Approve acquires nothing — and the claim path's capacity check excludes a member's own booking rows anyway (§6.6 self-exclusion), so routing approve through that capacity check would add no protection.

Parameters

ParameterTypeDescription
bookingIdstring-
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; }-
opts.actorIdstring-
opts.allowLateCancel?booleanAdmin late-cancel override (spec §7): when true, cancel skips the rules-engine cancellation policy guard and records a cancelled_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook (§6.6). The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than cancel.
opts.allowOutsideHours?booleanAdmin outside-hours override (spec §6.9): when true, claiming transitions skip the window-containment check and record an hours_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook. The CONSUMER owns the permission decision.
opts.allowOverbook?booleanAdmin overbook override (spec §6.6): when true, claiming transitions skip the capacity/overlap check and record an overbooked_by_admin timeline event instead. The CONSUMER owns the permission decision — taproot is auth-agnostic and only honors + records the flag.
opts.allowTransfer?booleanAdmin transfer override (spec §7.1): when true, the transfer/reschedule family skips the rules-engine policy guard for the transfer event and records a transfer_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowLateCancel/allowOverbook. The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than transfer.
opts.expectedVersion?number-
opts.reason?string-

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>

Inherited from

TransferService.approve


assertPolicyAllows()

protected assertPolicyAllows(
   booking: ComposedBookingRow, 
   event: string, 
   opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
}
): Promise<void>;

Defined in: server/bookings/booking-service.ts:955

POLICY seam (§8.1 step a / §8.3): approval routing from the PINNED policy bag (§6.2) + domain: "bookings" rules evaluation — throws PolicyDeniedError on deny. Protected so a consumer subclass can extend/replace the policy layer.

Parameters

ParameterTypeDescription
bookingComposedBookingRow-
eventstring-
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; }-
opts.actorIdstring-
opts.allowLateCancel?booleanAdmin late-cancel override (spec §7): when true, cancel skips the rules-engine cancellation policy guard and records a cancelled_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook (§6.6). The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than cancel.
opts.allowOutsideHours?booleanAdmin outside-hours override (spec §6.9): when true, claiming transitions skip the window-containment check and record an hours_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook. The CONSUMER owns the permission decision.
opts.allowOverbook?booleanAdmin overbook override (spec §6.6): when true, claiming transitions skip the capacity/overlap check and record an overbooked_by_admin timeline event instead. The CONSUMER owns the permission decision — taproot is auth-agnostic and only honors + records the flag.
opts.allowTransfer?booleanAdmin transfer override (spec §7.1): when true, the transfer/reschedule family skips the rules-engine policy guard for the transfer event and records a transfer_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowLateCancel/allowOverbook. The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than transfer.
opts.expectedVersion?number-
opts.reason?string-

Returns

Promise<void>

Inherited from

TransferService.assertPolicyAllows


cancel()

cancel(bookingId: string, opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
} & CascadeOptions): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/waitlist-service.ts:117

Cancel (§7) that also drives the capacity-frees CASCADE (§6.10): a confirmed/offered/held booking releasing its slot frees capacity, so after the cancel commits, offer that freed capacity to the next fitting waitlist party. The base cancel (release + refund_due) is unchanged; this override only adds the follow-on cascade on the released resource.

The cascade reads the POST-cancel blocking SUM (cancel flipped is_blocking=false in the same tx, §6.6), so the just-freed unit correctly reads as free — the "freed capacity must read free or the waitlist deadlocks" regression.

Cancelling a booking that is itself waiting (waitlisted/offered) also terminalizes ITS active offer row — see below.

Parameters

ParameterType
bookingIdstring
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; } & CascadeOptions

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>

Overrides

TransferService.cancel


cascadeOffers()

cascadeOffers(resourceId: string, opts?: CascadeOptions): Promise<void>;

Defined in: server/bookings/waitlist-service.ts:522

The capacity-frees CASCADE (§6.10). Under the resource's overlap-set lock, walk the FIFO party queue head-first and offer each party whose whole demand fits the currently-free capacity — strict FIFO (a non-fitting head blocks the queue). Offering flips a party's offers to offered (blocking), so free capacity is recomputed as we go. A party is never split.

Call this whenever capacity frees on a resource (a confirmed/offered booking cancels or expires); the primitive is idempotent — an offered party is no longer queued, so a re-run never double-offers.

BOUNDED WORK: at most maxOffersPerCascade parties per call (default DEFAULT_MAX_OFFERS_PER_CASCADE). This runs inline on cancel/reject/decline/expiry and every pass re-reads the queued list, so an uncapped loop would let one transition do work proportional to queue length × parties offered. Hitting the cap is not lossy — the remainder keeps its FIFO position for the next trigger.

FAIRNESS TRADE-OFF: each pass offers at most one party in its own transaction, then releases the lock and re-reads. A concurrent claim can interleave between parties, so a party is only ever offered capacity that was free at the head of ITS pass — the queue is never re-ordered, but a slot a non-fitting head just declined isn't held across passes.

Parameters

ParameterType
resourceIdstring
optsCascadeOptions

Returns

Promise<void>


complete()

complete(bookingId: string, opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/booking-service.ts:401

Parameters

ParameterTypeDescription
bookingIdstring-
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; }-
opts.actorIdstring-
opts.allowLateCancel?booleanAdmin late-cancel override (spec §7): when true, cancel skips the rules-engine cancellation policy guard and records a cancelled_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook (§6.6). The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than cancel.
opts.allowOutsideHours?booleanAdmin outside-hours override (spec §6.9): when true, claiming transitions skip the window-containment check and record an hours_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook. The CONSUMER owns the permission decision.
opts.allowOverbook?booleanAdmin overbook override (spec §6.6): when true, claiming transitions skip the capacity/overlap check and record an overbooked_by_admin timeline event instead. The CONSUMER owns the permission decision — taproot is auth-agnostic and only honors + records the flag.
opts.allowTransfer?booleanAdmin transfer override (spec §7.1): when true, the transfer/reschedule family skips the rules-engine policy guard for the transfer event and records a transfer_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowLateCancel/allowOverbook. The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than transfer.
opts.expectedVersion?number-
opts.reason?string-

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>

Inherited from

TransferService.complete


completeBooking()

completeBooking(bookingId: string, opts: {
  actorId: string | null;
  expectedVersion?: number;
  idempotencyKey?: string;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/booking-service.ts:456

System-actor confirmed→completed transition used by the completion sweeper (§11). Structural guard (endsAt <= now) must pass; the host is hydrated inside applyTransition from the schedule join.

Parameters

ParameterType
bookingIdstring
opts{ actorId: string | null; expectedVersion?: number; idempotencyKey?: string; }
opts.actorIdstring | null
opts.expectedVersion?number
opts.idempotencyKey?string

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>

Inherited from

TransferService.completeBooking


confirm()

confirm(bookingId: string, opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/booking-service.ts:293

Parameters

ParameterTypeDescription
bookingIdstring-
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; }-
opts.actorIdstring-
opts.allowLateCancel?booleanAdmin late-cancel override (spec §7): when true, cancel skips the rules-engine cancellation policy guard and records a cancelled_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook (§6.6). The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than cancel.
opts.allowOutsideHours?booleanAdmin outside-hours override (spec §6.9): when true, claiming transitions skip the window-containment check and record an hours_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook. The CONSUMER owns the permission decision.
opts.allowOverbook?booleanAdmin overbook override (spec §6.6): when true, claiming transitions skip the capacity/overlap check and record an overbooked_by_admin timeline event instead. The CONSUMER owns the permission decision — taproot is auth-agnostic and only honors + records the flag.
opts.allowTransfer?booleanAdmin transfer override (spec §7.1): when true, the transfer/reschedule family skips the rules-engine policy guard for the transfer event and records a transfer_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowLateCancel/allowOverbook. The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than transfer.
opts.expectedVersion?number-
opts.reason?string-

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>

Inherited from

TransferService.confirm


confirmMany()

confirmMany(bookingIds: readonly string[], opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}[]>;

Defined in: server/bookings/booking-service.ts:302

Batch confirm (§6.10): one transaction, one savepoint, all-or-none.

Parameters

ParameterTypeDescription
bookingIdsreadonly string[]-
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; }-
opts.actorIdstring-
opts.allowLateCancel?booleanAdmin late-cancel override (spec §7): when true, cancel skips the rules-engine cancellation policy guard and records a cancelled_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook (§6.6). The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than cancel.
opts.allowOutsideHours?booleanAdmin outside-hours override (spec §6.9): when true, claiming transitions skip the window-containment check and record an hours_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook. The CONSUMER owns the permission decision.
opts.allowOverbook?booleanAdmin overbook override (spec §6.6): when true, claiming transitions skip the capacity/overlap check and record an overbooked_by_admin timeline event instead. The CONSUMER owns the permission decision — taproot is auth-agnostic and only honors + records the flag.
opts.allowTransfer?booleanAdmin transfer override (spec §7.1): when true, the transfer/reschedule family skips the rules-engine policy guard for the transfer event and records a transfer_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowLateCancel/allowOverbook. The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than transfer.
opts.expectedVersion?number-
opts.reason?string-

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }[]>

Inherited from

TransferService.confirmMany


create()

create(raw: {
  bookingTypeId: string;
  capacityUnits?: number;
  organizationId: string;
  partyEmail?: string;
  partyName?: string;
  partyRef?: string;
  payload?: Record<string, unknown>;
  schedule?: {
     endsAt: unknown;
     resourceId: string;
     startsAt: unknown;
     timeZone: string;
  };
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/booking-service.ts:155

Create a draft booking, pinning the type's published version (§6.2) and appending booking_created + terms_pinned timeline rows — one tx.

When schedule is provided, a BookingSchedules row is written at isBlocking: false — a draft never blocks capacity (spec §6.6 truth table).

Parameters

ParameterType
raw{ bookingTypeId: string; capacityUnits?: number; organizationId: string; partyEmail?: string; partyName?: string; partyRef?: string; payload?: Record<string, unknown>; schedule?: { endsAt: unknown; resourceId: string; startsAt: unknown; timeZone: string; }; }
raw.bookingTypeIdstring
raw.capacityUnits?number
raw.organizationIdstring
raw.partyEmail?string
raw.partyName?string
raw.partyRef?string
raw.payload?Record<string, unknown>
raw.schedule?{ endsAt: unknown; resourceId: string; startsAt: unknown; timeZone: string; }
raw.schedule.endsAtunknown
raw.schedule.resourceIdstring
raw.schedule.startsAtunknown
raw.schedule.timeZonestring

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>

Inherited from

TransferService.create


decideRun()

decideRun(resourceId: string, opts: {
  actorId: string | null;
  bookingTypeVersionId: string;
  from: Date;
  to: Date;
}): Promise<{
  cancelled: number;
  enrolled: number;
  ran: boolean;
}>;

Defined in: server/bookings/waitlist-service.ts:676

Run-decision (§6.10 / Phase 2): decide whether a program offering runs at its enrollment deadline. Reads the pinned minEnrollment policy knob; if the confirmed enrollment (SUM(capacity_units) of confirmed bookings on the offering's window) is BELOW it, the class doesn't make — so every still-active booking on that window (confirmed/offered/waitlisted/held/ pending) is auto-cancelled through the machine (each a real cancel transition that releases capacity + records a run_cancelled reason).

No minEnrollment (or enrollment ≥ the minimum) ⇒ the offering runs and nothing is cancelled. Idempotent by construction: once cancelled the bookings leave the active set, so a re-run finds nothing to cancel.

CONCURRENCY: the enrollment read, the decision, and the teardown run under ONE transaction holding the resource's overlap-set lock (the same lock the claim/accept path takes). Without it a concurrent acceptOffer lifting the class to quorum after the enrollment snapshot would still be torn down.

OFFER TEARDOWN: each cancelled booking's active offer is terminalized in the same tx — super.cancel has no offer mutate hook, so a bare cancel would leave an orphan row the expiry sweeper loops on. No re-queue: it's terminal.

Parameters

ParameterType
resourceIdstring
opts{ actorId: string | null; bookingTypeVersionId: string; from: Date; to: Date; }
opts.actorIdstring | null
opts.bookingTypeVersionIdstring
opts.fromDate
opts.toDate

Returns

Promise<{ cancelled: number; enrolled: number; ran: boolean; }>

{ ran: boolean; enrolled: number; cancelled: number }.


declineOffer()

declineOffer(bookingId: string, opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
} & CascadeOptions): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/waitlist-service.ts:216

Public decline (§7). Terminalizes the whole PARTY's active offers (declined, booking → waitlisted) as a unit, then cascades the freed capacity to the next fitting party (§6.10).

The declining member's OWN outcome is propagated, not assumed: if its decline did not apply — the offer is no longer active because a concurrent acceptOffer won the version-CAS first (terminalizeOfferParty swallows the Stale/InvalidTransition for best-effort party unity) — this throws InvalidTransitionError rather than reporting the now-confirmed booking as a "successful decline".

Parameters

ParameterType
bookingIdstring
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; } & CascadeOptions

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>


declineOfferBooking()

declineOfferBooking(bookingId: string, opts: {
  actorId: string | null;
  eventType?: "waitlist_offer_declined" | "waitlist_offer_expired";
  expectedVersion?: number;
  idempotencyKey?: string;
  terminalStatus?: "expired" | "declined";
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/waitlist-service.ts:989

offered→waitlisted — the system/consumer decline/expire (§6.4/§7). The booking's machine edge is offered --declineOffer|expireOffer--> waitlisted (active-WAITING, NOT terminal), so the member must remain on the waitlist. This terminalizes the current offer row (declined/expired) in the same tx as the status flip and appends waitlist_offer_declined/_expired, but DOES NOT re-mint the member's queued row here — the caller (WaitlistService) re-queues the party AFTER cascading the freed capacity, so the just-declined party never re-grabs the slot it just declined (the without livelock; see requeueParty).

This is the per-booking primitive both the public declineOffer and the offer-expiry sweeper reuse. On a version-CAS loss / illegal edge it throws StaleBookingError/InvalidTransitionError (the party terminalizer skips such members best-effort; declineOffer propagates the head's outcome).

Parameters

ParameterType
bookingIdstring
opts{ actorId: string | null; eventType?: "waitlist_offer_declined" | "waitlist_offer_expired"; expectedVersion?: number; idempotencyKey?: string; terminalStatus?: "expired" | "declined"; }
opts.actorIdstring | null
opts.eventType?"waitlist_offer_declined" | "waitlist_offer_expired"
opts.expectedVersion?number
opts.idempotencyKey?string
opts.terminalStatus?"expired" | "declined"

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>


expireApproval()

expireApproval(bookingId: string, opts: {
  actorId: string | null;
  expectedVersion?: number;
  idempotencyKey?: string;
} & CascadeOptions): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/waitlist-service.ts:186

Approval-TTL expiry (§11) that also drives the capacity-frees CASCADE. The pending→expired sweep releases capacity for the same reason reject does — see above. Driven by LifecycleSweepers.approvalExpiry().

Parameters

ParameterType
bookingIdstring
opts{ actorId: string | null; expectedVersion?: number; idempotencyKey?: string; } & CascadeOptions

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>

Overrides

TransferService.expireApproval


expireHold()

expireHold(bookingId: string, opts: {
  actorId: string | null;
  expectedVersion?: number;
  idempotencyKey?: string;
  reason?: string;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/booking-service.ts:523

System-actor held→expired transition used by the hold sweeper (§6.3).

Uses the same version-CAS path as every other transition — the sweeper CAS races a concurrent confirm fairly: whichever commits first wins; the loser throws StaleBookingError and is skipped (§6.6 / §14).

actorId is null for the hold sweeper (system actor). expectedVersion is the version hydrated by the sweeper to participate in the CAS race.

Parameters

ParameterType
bookingIdstring
opts{ actorId: string | null; expectedVersion?: number; idempotencyKey?: string; reason?: string; }
opts.actorIdstring | null
opts.expectedVersion?number
opts.idempotencyKey?string
opts.reason?string

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>

Inherited from

TransferService.expireHold


expireOfferParty()

expireOfferParty(bookingId: string, opts: {
  actorId: string | null;
  expectedVersion?: number;
  offerExpiresAt: Date | null;
  resourceId: string | null;
} & CascadeOptions): Promise<void>;

Defined in: server/bookings/waitlist-service.ts:253

The offer-expiry sweeper arm's per-offer action (§11): terminalize the expired offer's whole PARTY (expired, booking → waitlisted) as a unit, then cascade the freed capacity to the next fitting party (§6.10). Each member carries a deterministic idempotency key so a sweeper re-run appends nothing new. Called by LifecycleSweepers.offerExpiry().

Parameters

ParameterType
bookingIdstring
opts{ actorId: string | null; expectedVersion?: number; offerExpiresAt: Date | null; resourceId: string | null; } & CascadeOptions

Returns

Promise<void>


generateUid()

protected generateUid(repos: BookingRepositories, organizationId: string): Promise<string>;

Defined in: server/bookings/booking-service.ts:996

8-char external short code, retried on org-uniqueness collision.

Parameters

ParameterType
reposBookingRepositories
organizationIdstring

Returns

Promise<string>

Inherited from

TransferService.generateUid


hold()

hold(bookingId: string, opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  holdTtlSeconds: number;
  reason?: string;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/booking-service.ts:260

draft→held: sets holdExpiresAt = now + ttl in the same CAS update.

Parameters

ParameterTypeDescription
bookingIdstring-
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; holdTtlSeconds: number; reason?: string; }-
opts.actorIdstring-
opts.allowLateCancel?booleanAdmin late-cancel override (spec §7): when true, cancel skips the rules-engine cancellation policy guard and records a cancelled_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook (§6.6). The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than cancel.
opts.allowOutsideHours?booleanAdmin outside-hours override (spec §6.9): when true, claiming transitions skip the window-containment check and record an hours_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook. The CONSUMER owns the permission decision.
opts.allowOverbook?booleanAdmin overbook override (spec §6.6): when true, claiming transitions skip the capacity/overlap check and record an overbooked_by_admin timeline event instead. The CONSUMER owns the permission decision — taproot is auth-agnostic and only honors + records the flag.
opts.allowTransfer?booleanAdmin transfer override (spec §7.1): when true, the transfer/reschedule family skips the rules-engine policy guard for the transfer event and records a transfer_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowLateCancel/allowOverbook. The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than transfer.
opts.expectedVersion?number-
opts.holdTtlSecondsnumber-
opts.reason?string-

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>

Inherited from

TransferService.hold


holdMany()

holdMany(bookingIds: readonly string[], opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  holdTtlSeconds: number;
  reason?: string;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}[]>;

Defined in: server/bookings/booking-service.ts:272

Batch hold (§6.10): one transaction, one savepoint, all-or-none. Every member's structural + capacity check is evaluated BEFORE any transition applies, and the whole group shares ONE holdExpiresAt stamp so the hold sweeper can expire it as a unit.

Parameters

ParameterTypeDescription
bookingIdsreadonly string[]-
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; holdTtlSeconds: number; reason?: string; }-
opts.actorIdstring-
opts.allowLateCancel?booleanAdmin late-cancel override (spec §7): when true, cancel skips the rules-engine cancellation policy guard and records a cancelled_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook (§6.6). The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than cancel.
opts.allowOutsideHours?booleanAdmin outside-hours override (spec §6.9): when true, claiming transitions skip the window-containment check and record an hours_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook. The CONSUMER owns the permission decision.
opts.allowOverbook?booleanAdmin overbook override (spec §6.6): when true, claiming transitions skip the capacity/overlap check and record an overbooked_by_admin timeline event instead. The CONSUMER owns the permission decision — taproot is auth-agnostic and only honors + records the flag.
opts.allowTransfer?booleanAdmin transfer override (spec §7.1): when true, the transfer/reschedule family skips the rules-engine policy guard for the transfer event and records a transfer_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowLateCancel/allowOverbook. The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than transfer.
opts.expectedVersion?number-
opts.holdTtlSecondsnumber-
opts.reason?string-

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }[]>

Inherited from

TransferService.holdMany


joinWaitlist()

joinWaitlist(bookingId: string, opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/waitlist-service.ts:812

draft→waitlisted (§6.4/§7). Non-claiming: waitlisted does NOT block capacity (§6.6 truth table), so this is a plain version-CAS transition — it never contends for a slot. In the SAME transaction it creates the booking's queued waitlist_offers row (the FIFO queue entry, keyed on registeredAt) and appends a waitlist_joined timeline event.

The cascade that later OFFERS this booking a freed slot lives in WaitlistService (waitlist-service.ts); joinWaitlist only enqueues.

Parameters

ParameterTypeDescription
bookingIdstring-
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; }-
opts.actorIdstring-
opts.allowLateCancel?booleanAdmin late-cancel override (spec §7): when true, cancel skips the rules-engine cancellation policy guard and records a cancelled_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook (§6.6). The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than cancel.
opts.allowOutsideHours?booleanAdmin outside-hours override (spec §6.9): when true, claiming transitions skip the window-containment check and record an hours_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook. The CONSUMER owns the permission decision.
opts.allowOverbook?booleanAdmin overbook override (spec §6.6): when true, claiming transitions skip the capacity/overlap check and record an overbooked_by_admin timeline event instead. The CONSUMER owns the permission decision — taproot is auth-agnostic and only honors + records the flag.
opts.allowTransfer?booleanAdmin transfer override (spec §7.1): when true, the transfer/reschedule family skips the rules-engine policy guard for the transfer event and records a transfer_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowLateCancel/allowOverbook. The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than transfer.
opts.expectedVersion?number-
opts.reason?string-

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>


machineFor()

protected machineFor(vertical: string): ErasedMachine;

Defined in: server/bookings/booking-service.ts:138

Checked vertical → composed-machine lookup; throws UnknownBookingTypeError.

Parameters

ParameterType
verticalstring

Returns

ErasedMachine

Inherited from

TransferService.machineFor


markNoShow()

markNoShow(bookingId: string, opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/booking-service.ts:409

Parameters

ParameterTypeDescription
bookingIdstring-
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; }-
opts.actorIdstring-
opts.allowLateCancel?booleanAdmin late-cancel override (spec §7): when true, cancel skips the rules-engine cancellation policy guard and records a cancelled_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook (§6.6). The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than cancel.
opts.allowOutsideHours?booleanAdmin outside-hours override (spec §6.9): when true, claiming transitions skip the window-containment check and record an hours_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook. The CONSUMER owns the permission decision.
opts.allowOverbook?booleanAdmin overbook override (spec §6.6): when true, claiming transitions skip the capacity/overlap check and record an overbooked_by_admin timeline event instead. The CONSUMER owns the permission decision — taproot is auth-agnostic and only honors + records the flag.
opts.allowTransfer?booleanAdmin transfer override (spec §7.1): when true, the transfer/reschedule family skips the rules-engine policy guard for the transfer event and records a transfer_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowLateCancel/allowOverbook. The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than transfer.
opts.expectedVersion?number-
opts.reason?string-

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>

Inherited from

TransferService.markNoShow


markNoShowBySystem()

markNoShowBySystem(bookingId: string, opts: {
  actorId: string | null;
  expectedVersion?: number;
  idempotencyKey?: string;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/booking-service.ts:487

System-actor confirmed→no_show transition used by the no-show sweeper (§11). No structural guard beyond status check — the sweeper is responsible for enforcing the grace window before calling this.

Parameters

ParameterType
bookingIdstring
opts{ actorId: string | null; expectedVersion?: number; idempotencyKey?: string; }
opts.actorIdstring | null
opts.expectedVersion?number
opts.idempotencyKey?string

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>

Inherited from

TransferService.markNoShowBySystem


reject()

reject(bookingId: string, opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
} & CascadeOptions): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/waitlist-service.ts:163

Reject (§7) that also drives the capacity-frees CASCADE (§6.10).

pending BLOCKS capacity and rejected does not (§6.6), so a reviewer turning a booking down releases a seat exactly like a cancel — and on a requiresApproval type that seat may be one the waitlist itself just handed out, since acceptOffer routes those parties into pending for review. Without this the next party is never offered and the freed seat sits idle until some unrelated transition happens to cascade.

Parameters

ParameterType
bookingIdstring
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; } & CascadeOptions

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>

Overrides

TransferService.reject


reschedule()

reschedule(bookingId: string, opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  endsAt?: Date;
  expectedVersion?: number;
  reason?: string;
  resourceId?: string;
  startsAt?: Date;
  timeZone?: string;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/transfer-service.ts:186

Reschedule convenience wrapper (§7.1/§11): a slot-only transfer. The source carries a rescheduled (not transferred) event.

Parameters

ParameterTypeDescription
bookingIdstring-
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; endsAt?: Date; expectedVersion?: number; reason?: string; resourceId?: string; startsAt?: Date; timeZone?: string; }-
opts.actorIdstring-
opts.allowLateCancel?booleanAdmin late-cancel override (spec §7): when true, cancel skips the rules-engine cancellation policy guard and records a cancelled_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook (§6.6). The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than cancel.
opts.allowOutsideHours?booleanAdmin outside-hours override (spec §6.9): when true, claiming transitions skip the window-containment check and record an hours_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook. The CONSUMER owns the permission decision.
opts.allowOverbook?booleanAdmin overbook override (spec §6.6): when true, claiming transitions skip the capacity/overlap check and record an overbooked_by_admin timeline event instead. The CONSUMER owns the permission decision — taproot is auth-agnostic and only honors + records the flag.
opts.allowTransfer?booleanAdmin transfer override (spec §7.1): when true, the transfer/reschedule family skips the rules-engine policy guard for the transfer event and records a transfer_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowLateCancel/allowOverbook. The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than transfer.
opts.endsAt?Date-
opts.expectedVersion?number-
opts.reason?string-
opts.resourceId?string-
opts.startsAt?Date-
opts.timeZone?string-

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>

Inherited from

TransferService.reschedule


sendWaitlistOffer()

sendWaitlistOffer(bookingId: string, opts: {
  actorId: string | null;
  expectedVersion?: number;
  idempotencyKey?: string;
  offerExpiresAt: Date | null;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/waitlist-service.ts:862

waitlisted→offered — the system-driven offer send used by the capacity- frees cascade (§6.10). Flips the booking's active queued offer row to offered (stamping offeredAt + the offerExpiresAt accept-by deadline) in the same tx as the status flip, and enqueues an offer_send_due outbox row the dispatcher fans out through NotificationPort. offered BLOCKS capacity (§6.6 truth table decision #2), earmarking the freed slot so the party's later acceptOffer cannot lose a race.

Parameters

ParameterType
bookingIdstring
opts{ actorId: string | null; expectedVersion?: number; idempotencyKey?: string; offerExpiresAt: Date | null; }
opts.actorIdstring | null
opts.expectedVersion?number
opts.idempotencyKey?string
opts.offerExpiresAtDate | null

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>


submit()

submit(bookingId: string, opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/booking-service.ts:358

draft→pending — a CLAIMING transition (§6.6 truth table: pending BLOCKS capacity), so it runs the claim path: overlap-set lock + batch capacity check + window containment. On the old non-claiming path two concurrent submits could both reach pending and double-book the slot.

Parameters

ParameterTypeDescription
bookingIdstring-
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; }-
opts.actorIdstring-
opts.allowLateCancel?booleanAdmin late-cancel override (spec §7): when true, cancel skips the rules-engine cancellation policy guard and records a cancelled_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook (§6.6). The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than cancel.
opts.allowOutsideHours?booleanAdmin outside-hours override (spec §6.9): when true, claiming transitions skip the window-containment check and record an hours_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook. The CONSUMER owns the permission decision.
opts.allowOverbook?booleanAdmin overbook override (spec §6.6): when true, claiming transitions skip the capacity/overlap check and record an overbooked_by_admin timeline event instead. The CONSUMER owns the permission decision — taproot is auth-agnostic and only honors + records the flag.
opts.allowTransfer?booleanAdmin transfer override (spec §7.1): when true, the transfer/reschedule family skips the rules-engine policy guard for the transfer event and records a transfer_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowLateCancel/allowOverbook. The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than transfer.
opts.expectedVersion?number-
opts.reason?string-

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>

Inherited from

TransferService.submit


toHost()

protected toHost(b: ComposedBookingRow): ErasedBookingHost;

Defined in: server/bookings/booking-service.ts:981

Map the hydrated row to the composed host the machine guards read. startsAt/endsAt/timeZone/resourceId are sourced from the joined schedule row (§5.2 hydration spec). When no schedule exists (draft with no schedule), endsAt is undefined and the completable guard (endsAt <= now) correctly fails, so complete is unreachable — by design.

offerExpiresAt is surfaced from the ACTIVE waitlist offer row (§6.4) so the §5.2 offered→confirmed structural guard reads the real accept-by deadline off the host — not undefined from a bare row (a hydration bug). offers is filtered to active rows at hydration; the single offered/ queued row's deadline (or null when none) is what the guard sees. Protected so the transfer family (§7.1) reuses the same hydration.

Parameters

ParameterType
bComposedBookingRow

Returns

ErasedBookingHost

Inherited from

TransferService.toHost


transfer()

transfer(bookingId: string, opts: Omit<{
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
  to: {
     bookingTypeId?: string;
     capacityUnits?: number;
     groupId?: string | null;
     partyEmail?: string | null;
     partyName?: string | null;
     partyRef?: string | null;
     schedule?: {
        endsAt?: Date;
        resourceId?: string;
        startsAt?: Date;
        timeZone?: string;
     };
  };
}, "to"> & {
  to: {
     bookingTypeId?: string;
     capacityUnits?: number;
     groupId?: string | null;
     partyEmail?: string | null;
     partyName?: string | null;
     partyRef?: string | null;
     schedule?: {
        endsAt?: Date;
        resourceId?: string;
        startsAt?: Date;
        timeZone?: string;
     };
  };
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}>;

Defined in: server/bookings/transfer-service.ts:167

Transfer one booking along the to delta (§7.1) — returns the successor (or the same booking for an in-place repoint). The batch of one.

Parameters

ParameterType
bookingIdstring
optsOmit<{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; to: { bookingTypeId?: string; capacityUnits?: number; groupId?: string | null; partyEmail?: string | null; partyName?: string | null; partyRef?: string | null; schedule?: { endsAt?: Date; resourceId?: string; startsAt?: Date; timeZone?: string; }; }; }, "to"> & { to: { bookingTypeId?: string; capacityUnits?: number; groupId?: string | null; partyEmail?: string | null; partyName?: string | null; partyRef?: string | null; schedule?: { endsAt?: Date; resourceId?: string; startsAt?: Date; timeZone?: string; }; }; }

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }>

Inherited from

TransferService.transfer


transferMany()

transferMany(members: readonly TransferMember[], opts: {
  actorId: string;
  allowLateCancel?: boolean;
  allowOutsideHours?: boolean;
  allowOverbook?: boolean;
  allowTransfer?: boolean;
  expectedVersion?: number;
  reason?: string;
}): Promise<{
  amountOwedCents: number | null;
  bookingTypeId: string;
  bookingTypeVersionId: string;
  capacityUnits: number;
  createdAt: Date;
  createdBy: string | null;
  currency: string | null;
  deletedAt: Date | null;
  deletedBy: string | null;
  groupId: string | null;
  holdExpiresAt: Date | null;
  id: string;
  organizationId: string;
  origin: BookingOrigin;
  paid: boolean;
  partyEmail: string | null;
  partyName: string | null;
  partyRef: string | null;
  payload: JsonValue;
  paymentRef: string | null;
  pendingExpiresAt: Date | null;
  status: BookingStatus;
  statusUpdatedAt: Date;
  statusUpdatedBy: string | null;
  supersededById: string | null;
  uid: string;
  updatedAt: Date;
  updatedBy: string | null;
  version: number;
}[]>;

Defined in: server/bookings/transfer-service.ts:212

Move a batch of bookings atomically (§7.1): all-or-none, savepoint- wrapped, capacity-checked over the UNION overlap set with excludeBookingIds = the sources — the wedding (hall + kitchen + parking) moves whole or not at all. Returns the successors (or the same booking for in-place members) in member order.

Parameters

ParameterTypeDescription
membersreadonly TransferMember[]-
opts{ actorId: string; allowLateCancel?: boolean; allowOutsideHours?: boolean; allowOverbook?: boolean; allowTransfer?: boolean; expectedVersion?: number; reason?: string; }-
opts.actorIdstring-
opts.allowLateCancel?booleanAdmin late-cancel override (spec §7): when true, cancel skips the rules-engine cancellation policy guard and records a cancelled_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook (§6.6). The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than cancel.
opts.allowOutsideHours?booleanAdmin outside-hours override (spec §6.9): when true, claiming transitions skip the window-containment check and record an hours_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowOverbook. The CONSUMER owns the permission decision.
opts.allowOverbook?booleanAdmin overbook override (spec §6.6): when true, claiming transitions skip the capacity/overlap check and record an overbooked_by_admin timeline event instead. The CONSUMER owns the permission decision — taproot is auth-agnostic and only honors + records the flag.
opts.allowTransfer?booleanAdmin transfer override (spec §7.1): when true, the transfer/reschedule family skips the rules-engine policy guard for the transfer event and records a transfer_overridden_by_admin timeline event instead — the same bypass-and-record contract as allowLateCancel/allowOverbook. The CONSUMER owns the permission decision; taproot only honors + records the flag. Ignored on every event other than transfer.
opts.expectedVersion?number-
opts.reason?string-

Returns

Promise<{ amountOwedCents: number | null; bookingTypeId: string; bookingTypeVersionId: string; capacityUnits: number; createdAt: Date; createdBy: string | null; currency: string | null; deletedAt: Date | null; deletedBy: string | null; groupId: string | null; holdExpiresAt: Date | null; id: string; organizationId: string; origin: BookingOrigin; paid: boolean; partyEmail: string | null; partyName: string | null; partyRef: string | null; payload: JsonValue; paymentRef: string | null; pendingExpiresAt: Date | null; status: BookingStatus; statusUpdatedAt: Date; statusUpdatedBy: string | null; supersededById: string | null; uid: string; updatedAt: Date; updatedBy: string | null; version: number; }[]>

Inherited from

TransferService.transferMany

On this page