Skip to main content

Extending journeys with third-party services

Journeys can include externally quoted ancillary services without pretending they are native package components. Typical examples are travel insurance and destination eSIMs.

This page describes the stable architecture, API contract, and trust boundaries.

Architecture

Tenant UI
│ requests quote with trip and traveller context

Journeys ancillary service
├─ tenant configuration and provider adapter
├─ normalized quote presentation
└─ tamper-resistant, tenant-scoped selection

├─ included in the Journeys pricing presentation


Checkout orchestration
├─ creates the booking through the platform
└─ writes the selected service as an ad-hoc itinerary item

Basket Service continues to hold native package selections, travellers, promotions, schedules, and payment requests. It does not store externally quoted ancillary items.

Provider adapters

A provider adapter translates between a provider's quote API and a normalized Journeys view. It is responsible for:

  • accepting only basket traveller IDs and bounded scalar metadata from the UI, then loading trip, departure, traveller date-of-birth, and currency context from the basket;
  • normalizing product name, description, price, currency, validity, and provider reference for presentation;
  • rejecting expired, malformed, or currency-incompatible quotes;
  • keeping provider credentials and raw provider payloads server-side.

Adapters quote; they do not grant tenant UIs authority to set price or provider references.

Selection and tamper resistance

When a guest selects an external service, Journeys records a server-issued selection reference bound to:

  • the tenant and basket;
  • the basket or checkout context;
  • the quoted product, price, currency, and validity;
  • the applicable basket travellers;
  • the provider adapter that issued the quote.

The browser can return the opaque selection reference, but it cannot replace the price, tenant, provider reference, or applicability. Journeys revalidates the selection before pricing and checkout. A selection from one tenant or basket must never be accepted in another.

Pricing presentation

The journey review can show an external ancillary alongside native package pricing, with a clear label and currency. Keep the boundary explicit:

  • native package lines and optional aggregate reseller commission come from BasketPricing;
  • payment-method surcharges come from Edge Pay;
  • external ancillary prices come from the validated Journeys ancillary selection.

The combined presentation must not imply that the external service is already a Basket Service component. If the quote expires or changes, Journeys returns an explicit re-quote requirement instead of silently changing the amount.

Checkout write-through

Checkout passes the validated selection into platform orchestration. The platform creates an ad-hoc itinerary item carrying the normalized service description, amount, currency, traveller applicability, provider reference, and audit context required by the adapter contract.

Booking creation and ancillary persistence form one explicit checkout outcome:

  • success means the booking and required ancillary write-through completed;
  • failure identifies whether quoting, validation, booking creation, provider confirmation, or itinerary-item persistence failed;
  • a required ancillary failure must not return a successful booking response with the service silently omitted;
  • retries must be idempotent so the same selection cannot create duplicate itinerary items.

Where a provider requires a separate post-booking confirmation or policy purchase, the adapter must make that state visible. The UI must not describe a quote as purchased until the provider confirmation succeeds.

Failure handling

FailureRequired behaviour
Quote provider unavailableReturn an explicit unavailable/retryable result; do not substitute a fabricated product or price.
Quote expiredRequire a new quote and selection before checkout.
Tenant or basket mismatchReject the selection.
Price/currency mismatchReject or re-quote; never trust a browser-supplied override.
Booking or write-through failureReturn an explicit failed checkout outcome. If booking already created the itinerary, retry resumes that booking and repeats the idempotent ancillary write; it does not report partial success.
Retry after an uncertain responseResolve by the provider, quote, and item idempotency key before attempting another write.

Examples

External insurance. The adapter quotes products for the trip dates, travellers, origin market, and basket currency. A selected quote is shown in the review and, after successful checkout/provider confirmation, written through as an ad-hoc itinerary item with the provider policy reference.

eSIM. The adapter quotes a regional data plan for the destination and travel dates. The selection is tenant-scoped and persisted as an ad-hoc itinerary item; fulfilment status remains explicit if activation happens asynchronously.

Current API boundary

Quote a configured provider:

POST /api/journeys/v1/baskets/{basketId}/external-ancillaries/{providerId}/quotes?tenantId={tenant}
Content-Type: application/json

{
"traveller_ids": ["1", "2"],
"metadata": { "market": "AU" }
}

The provider response is normalized to quote items with id, provider_ref, label, unit_amount, quantity limits, and optional bounded metadata. Journeys returns an opaque quote_token.

Select a quoted item:

POST /api/journeys/v1/baskets/{basketId}/external-ancillaries/{providerId}/selections?tenantId={tenant}
Content-Type: application/json

{
"quote_token": "{opaque-token}",
"item_id": "standard-cover",
"quantity": 2,
"traveller_ids": ["1", "2"]
}

The returned selection contains fixed amount, currency, source, provider_ref, traveller_ids, and bounded metadata fields inside a signed selection_token.

Pass one or more externalAncillaryToken query parameters to GET /baskets/{basketId}/prices for the display-only combined total. Pass the same tokens as external_ancillary_selection_tokens when creating the booking. Full schemas are published in the OpenAPI contract.