booking.hofj.com, weebora.com, and terrarossa.com through a single, normalized HoJ
API surface. The brand is selected at every call with ?brand=; the resolved domain is
both the upstream host and the tenant routing value.
These routes are internal only. The calling client must have
profile=internal and
allowedEntities includes itineraries (cart manipulation), bookings (confirmation +
user-scoped lookup), and trips (user-scoped lists). Unknown brands are rejected against
the CMS distribution-channels list (see GET /v1/distribution-channels).How it works
Every response uses the standard HoJ envelope:meta.now is forwarded from the brand site (epoch ms) for clock-skew accounting on the
frontend. Errors are RFC 7807 problem-details (application/problem+json).
Authentication
- Internal client credentials: standard HoJ Bearer (API key or OAuth2 client_credentials).
Sent on every call as
Authorization: Bearer <key>. Required for all routes. - End-user Bearer (
X-End-User-Authorization): only required by user-scoped reads (GET /v1/bookings/:id,GET /v1/trips/:status,GET /v1/trips/active-summary). The proxy forwards it verbatim as the upstreamAuthorizationheader (the brand site expects the Cognito access token from the user’s session).
staging.weebora.com (or simply Weebora, resolved through /v1/distribution-channels).
1. Pre-flight — pick a product
The brand-siteproductId must match a product visible on the chosen brand. For internal
flows you can either look it up from the public listing or via trip code:
POST /v1/itineraries accepts either form for productId:
- numeric CMS id (e.g.
181) — matches theidfield ofGET /v1/products. - alphanumeric trip / provider code (e.g.
t0054825) — matchesproviderID/tripCodeon the internal DTO.
2. Create itinerary
itineraryId — it identifies the cart on every subsequent call.
3. Experience options (activities)
4. Accommodation
5. Customer info
6. Pax details
GET .../pax returns one slot per traveler with a stable refId. PUT .../pax accepts the
full array; each element MUST preserve its refId.
7. Payment
Optionally apply promo code / change currency, then create the Stripe payment intent. The brand site does not acceptGET on this route (it returns 405). Use POST
and send paymentType.
Do not send
"deposit" or "balance" as paymentType. Those are product names;
the brand validator only accepts "full" or "plan".
GET /v1/itineraries/{id}/payment still exists as a compatibility alias: the API
translates it to the brand POST and defaults paymentType to full (or honours
?paymentType= / ?planIndex=). New integrations should use POST.
Response:
data to Stripe.js:
8. Thank-you — confirm booking
After Stripe confirms the payment, confirm the reservation with the samepaymentType (and planIndex when using a plan). If you omit paymentType, the
API defaults to "full" before forwarding to the brand — a body of only
{ "itineraryId" } is enough for a full payment.
data is the human-readable reservation code — display it on the thank-you page.
Post-purchase lookups (user-scoped)
These routes are user-scoped on the brand site. Pass the end-user’s Cognito access token inX-End-User-Authorization:
Worked example — Magnifico Padel a Lanzarote
Product page: weebora.com/it/destinazioni/lanzarote/tocahub-lanzarote/magnifico-padel-a-lanzaroteError model
All errors areapplication/problem+json per RFC 7807:
Operational notes
- Idempotency: brand-site
POST /bookingis an upsert keyed byitineraryId— calling it twice after a successful payment is safe and returns the same reservation code. - Payment types: the brand validator only accepts
paymentType"full"|"plan". Deposit / balance isplan+planIndex. The API defaults omittedpaymentTypeto"full"on both payment and booking so a documented{ "itineraryId" }body still works. - Timeouts: default 15s per upstream call (
CHECKOUT_PROXY_TIMEOUT_MS). Activity / accommodation searches can be the slowest because they fan out to upstream availability. - Tenant routing: the same domain resolved from
?brand=is also sent asX-Nezasa-Channelto the CMS for content lookups. UseGET /v1/distribution-channelsto see all registered brand names and domains.
