Booking & Vouchers
Once allocation picks a hotel for each group, the booking worker reserves rooms with the provider, generates a voucher, and hands off to notifications.
Flow
For each allocation:
1. Idempotency check (caseUrn + waveUrn + groupId)
2. Try primary hotel with retries
- 5 attempts, exp backoff 2s → 32s
- Token refresh on 401, retry on 429 / 5xx
3. Fallback to next-ranked hotels (3 max, 1 attempt each)
4. On success:
- Decrement direct-contract inventory
- Generate voucher (HTML + text)
- Enqueue notification
5. On exhaustion:
- Create ManualReviewItem { category: BOOKING_FAILED, priority: HIGH }
- Transition case to MANUAL_REVIEW
Payment methods
Nexa supports two payment modes — per policy and per provider.
Corporate guarantee (default)
Used for airline-pays scenarios. The booking is placed with payment.method = GUARANTEE; the hotel holds the room but does not charge a card. Payment is settled via the airline's corporate account at check-out.
- Pros: no PCI surface, simpler reconciliation.
- Cons: some hotels refuse
GUARANTEEand need a card; those are filtered out at allocation time.
Tokenized card
For airports where corporate guarantee is not accepted, Nexa integrates with a PSP (payment service provider) to tokenize a corporate card. Only the PSP token is stored — never a PAN.
Enable per policy:
"acceptedPayments": ["CARD"],
"cardToken": "tok_xxxxxxxxxxxx"
Voucher
A voucher is generated after CONFIRMED and contains everything a front desk needs to honor the stay:
- Confirmation code
- Hotel name, address, phone
- Check-in / check-out dates and times
- Rooms reserved
- Passenger list (names as on the manifest, no sensitive IDs)
- Total cost + currency
- Payment instructions (corporate guarantee boilerplate or card reference)
- Any SSR special instructions (accessibility, connecting rooms, early check-in)
Fetch HTML or text:
curl https://us-central1.api.nexastudio.io/booking/reservations/$RESERVATION_URN/voucher/html
curl https://us-central1.api.nexastudio.io/booking/reservations/$RESERVATION_URN/voucher/text
Vouchers are deterministic: re-rendering the same reservation produces byte-identical output, which makes the idempotency key safe to re-use.
Retry logic in detail
Attempt 1 → provider call
on 429 → exp backoff, retry
on 401 → refresh token, retry
on 5xx → exp backoff, retry
on 4xx (not 401/429) → stop, return failure
Attempt N (up to 5) → same
Exhausted →
Try fallback hotel (rank 2) with single attempt
Try fallback hotel (rank 3) with single attempt
Try fallback hotel (rank 4) with single attempt
All failed → Manual Review
Idempotency is the safety net: the same (caseUrn, waveUrn, groupId) can be retried safely — if the provider already confirmed, Nexa returns the existing reservation rather than double-booking.
Manual booking
An operator can book a hotel directly, bypassing the allocation wave, for edge cases (a passenger insists on a specific property that passes policy):
curl -X POST https://us-central1.api.nexastudio.io/booking/manual \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"caseUrn": "urn:nexa:case:xyz",
"groupId": "GRP-3",
"hotelUrn": "urn:nexa:hotel:abc",
"providerOfferId": "ASDFGHJKL...",
"reason": "passenger preference — contract-direct Ibis MAD"
}'
Manual bookings still flow through the provider adapter; they simply skip ranking. Audit records the manual override with the reason.
Cancellations
- Amadeus Self-Service does not expose cancellations; those reservations have to be cancelled at the hotel or through Amadeus Enterprise.
- Hotelbeds supports cancellations; Nexa exposes
POST /booking/reservations/:urn/cancelfor operators. - Direct contracts always cancel immediately and restore inventory.
On cancellation:
- Reservation status →
CANCELLEDwith reason. - Direct-contract inventory is restored.
- Notifications send a cancellation email if the voucher was already delivered.
- Audit records the cancelling actor.