1. Portal and environment
- Production supplier credentials are added and the intended suppliers are enabled in the Production portal (Portal & Environments).
- A Production API key is created and stored in a secret manager, never in source control, client code or logs.
- Your Production backend uses
https://api.unifystays.com. Sandbox and Production keys, caches, stored IDs and bookings are fully separate. - A key rotation is rehearsed: deploy the new key, confirm traffic, then disable the old key.
- All API calls come from your backend. The room stream is opened server-side (browsers cannot send
x-api-keywithEventSource). - Owners or admins who will approve
AWAITING_REVIEWbookings have portal access and know the process (Create Booking).
2. Discovery and search
- Autocomplete is debounced, starts at 3 characters, and sends
place_idorhotel_id(never theidUI key). A hoteldestination.idis sent as a number. - The guest form uses the
iso_codefrom Nationalities, and the same nationality is sent in search and in rooms. You never rely on theINdefault. - Autocomplete (48 hours) and nationalities (30 days) are cached per Caching Strategy; prices and offers are not.
- Search uses
pricing_mode: "instant"for guest pages and polls price updates every 2 seconds only while results are visible, stopping onCOMPLETEorEXPIRED. - Cards render by
price.status(live,recent,checking,no_price,unpriced). You never show0or a placeholder whenamountisnull, and you handle unknown statuses as “no price”. - Search amounts are divided by 100 and displayed with
price.currency(not assumed USD). - An expired
search_id(404) triggers a fresh search. Paging keeps the same inputs, and filter or sort changes restart without a cursor.
3. Rooms and prebook
- Room search sends the same dates,
child_agesand nationality as the search, and openssse_stream_urlimmediately. - The stream reader renders offers as they arrive, stops at
done, closes the connection, reconnects withLast-Event-ID, and starts a new room search onROOM_STREAM_REPLAY_GAPorROOM_STREAM_NOT_FOUND. - A failing or slow supplier (
provider_error,provider_skipped,done.status = PARTIAL) does not discard the other offers. OnlyFAILEDoffers a retry. - Room cards are drawn from families and variants (Get Hotel Rooms), each row shows its own
provider_roombeds and maximum guests, andtmp_and room IDs are never stored or used as booking keys. - Prices from offers and prebook are read as
amount_minor / 10^currency_exponent;payable_at_propertyis shown separately as “pay at hotel”;commercialvalues are never added to a ledger. - Notices with
must_display: trueand the full cancellation policy are shown before the guest pays. - Prebook is called when checkout starts, with the
hotel_idandbooking_tokenfrom the same option. You handle all three decisions:READY_TO_BOOK,RECONFIRMATION_REQUIRED(guest must accept the new price) andNOT_BOOKABLE(back to rooms). - The checkout form is built from
booking_requirementsof the prebook response, with a countdown tooffer.lifecycle.expires_at.
4. Booking safety
- Every booking attempt has one unique
Idempotency-Key, generated and saved before the first request, up to 128 characters. - Retrying after a timeout uses the same key and the identical body, and you call Find Booking by Idempotency-Key when you have no response. A new key is used only for a deliberate new attempt.
-
data.booking_idis saved immediately next to your order number. - Create Booking sends every prebook
room_allocation_idexactly once, with guest counts matching each room’soccupancy(age 0 or 1 isINFANT), exactly one lead guest, and answers keyed by the prebookrequirement_idvalues. - The traveler’s public IP is sent in
X-Unifystays-End-User-IP. - You never show “confirmed” for
PROCESSINGorAWAITING_REVIEW, and never issue a voucher beforeCONFIRMED.AWAITING_REVIEWshows “room and price are not reserved”. - A
FAILEDbooking tells the guest and restarts from rooms with a new key;RECONCILIATION_REQUIREDnever triggers a second booking. - If you use
HOLD, a timer confirms beforehold_deadline_at, and payment is collected before Confirm Hold.
5. Tracking, cancellation and webhooks
- Webhooks are registered on the Production environment with a public HTTPS URL, tested with
webhook.test, and verified against the raw request body (Verify Webhook Signatures). - The receiver returns
2xxwithin 10 seconds, stores the eventidto ignore repeats, ignores unknown event types and fields, and applies events bydata.object.version. - Status polling remains as a fallback (wait
next_poll_after_ms, stop whenis_terminal). - The hotel confirmation number is shown when present; if it is
nullafterCONFIRMED, the guest sees “to follow” and you pick it up frombooking.updatedor a later read. - Cancellation is offered only when
available_actionscontainsCANCEL. It uses a fresh quote, shows the penalty (neverUNKNOWNas zero), sends a newIdempotency-Key, and says “cancelled” only atCANCELLED. - You alert on webhook
consecutive_failuresand onEXHAUSTEDdeliveries, and know how to replay them. - The webhook signing secret is in a secret manager and secret rotation is rehearsed.
6. Errors, limits and operations
- Your client branches on HTTP status and
error.code(Errors & Retries), never onmessagetext, and shows its own guest-friendly messages. - Expired
booking_token,prebooking_idand quote errors return the guest to a recoverable step, not a dead end. - Retries use exponential backoff with jitter, honour
Retry-After, and never retry a4xxunchanged.429 RATE_LIMITEDis handled andRateLimit-Remainingis monitored. - You log
X-Request-Id(orrequest_id) for every failed call and keep it in support tooling, with thebooking_idand idempotency key. - Guest personal data is not written to client-side logs or unprotected storage.
- The client ignores unknown fields and handles unknown enum values (Versioning).
- You have a support contact path for
CONTACT_SUPPORTbookings and for supplier setup errors (424).
7. Final production smoke test
- Autocomplete, search, price polling, rooms, prebook for a real Production destination.
- One real booking in a refundable offer with a cancellation window of more than 2 days, ending
CONFIRMED, with the webhook received and the voucher rendered. - The same booking cancelled through quote and cancel, ending
CANCELLED. - A deliberate duplicate request with the same
Idempotency-Keyreturns the original booking.
You are live
Once live, enable additional supported suppliers in the Unifystays portal. Your
application keeps the same API contract, search flow and booking flow.