Skip to main content

API architecture

Edge Journeys is a thin, tenant-facing API over the Kaptio commerce platform. Everything a booking experience needs — catalog, pricing, basket, promotions, payment, checkout — is exposed through one versioned HTTP surface, while the platform services behind it (KTAPI, Basket Service, Salesforce) stay invisible to your frontend.

This page explains how the layers fit together, how authentication works, and the difference between what is exposed today and what is exposable from the underlying platform.

The layers

Your UI (web, advisor portal, call centre, chatbot, AI agent)


Edge Journeys API (/api/journeys/v1/*)
│ Tenant-scoped wrapper. Validates config, normalizes errors,
│ caches catalog reads, emits metrics per tenant/channel.

├──► KTAPI ─────────────── Catalog, availability, pricing engine
│ /v1.0/packages/* Package search, departures, cruise cabins
│ or touring room options, prices

├──► Basket Service ────── Commerce session state
│ /baskets/* Basket lifecycle, component selections,
│ travellers, promotions, pricing,
│ payment schedules & requests, checkout

└──► Salesforce ────────── System of record
(via Basket Service) Checkout lands as a Kaptio itinerary;
booking reference returned to the UI

Two facts follow from this shape:

  1. Your frontend only ever talks to the Journeys API. One base URL, one error contract, one auth story. The wrapper handles service tokens, retries, and parallel fan-out to the platform behind the scenes.
  2. The wrapper is a curated view, not a ceiling. The platform underneath exposes a larger surface — package search, bulk pricing, inventory, reservations, promotion checks, and cancellation calculations. A platform capability is not automatically a public Journeys endpoint; it becomes callable only after a tenant-safe route and contract are added. See Platform API surface for the inventory.

Request flow: from browse to booking

A typical B2C booking makes this sequence of calls:

StepCallBacked by
Browse tripsGET /catalog/tripsKTAPI packages + departures + configured pricing model
Pick a categoryGET /catalog/categoriesTenant catalog config + KTAPI
Pick a roomGET /catalog/roomsKTAPI cruise cabin inventory or touring room-option inventory + prices
Start a basketPOST /basketsBasket Service
Select the roomPATCH /baskets/:id/componentsBasket Service
Add travellersPUT /baskets/:id/travellersBasket Service
Apply a promo codePOST /baskets/:id/promotionsBasket Service → KTAPI promotion check
Get the priceGET /baskets/:id/pricesBasket Service → KTAPI bulk pricing
Get deposit optionsGET /baskets/:id/payment-schedulesBasket Service → KTAPI payment schedules
Take paymentPOST /baskets/:id/payment-requestsPUT .../completeBasket Service + payment provider
BookPOST /baskets/:id/bookingsBasket Service → Salesforce itinerary

Every call is documented endpoint-by-endpoint in the Journeys API reference, and every object in the flow is defined in the Data model.

Authentication and tenant isolation

The Journeys API applies two layers of security:

At the edge. Every request carries a tenantId (query parameter or body field). The API resolves the tenant's journey.yaml before doing anything else — an unknown tenant is a 404, and every downstream call is scoped to that tenant's commercial context (channels, tax profile, account, package allow-list). Baskets are tenant-bound: reading a basket with the wrong tenantId returns 403, never another tenant's data.

Behind the wrapper. The Journeys service authenticates to platform APIs with OAuth 2.0 client-credentials tokens issued by Kaptio's identity provider (Keycloak), with separate audiences for Basket Service and KTAPI. These credentials live server-side only — no platform token ever reaches the browser.

Public-facing extras (like the AI quote endpoint) add per-IP and per-tenant rate limits and hard input caps.

Environments

EnvironmentBase URL pattern
Staginghttps://journeys.edge.staging.kaptioapis.com/api/journeys/v1
Productionhttps://journeys.edge.production.kaptioapis.com/api/journeys/v1

Custom booking domains (e.g. book.meridianvoyages.com) serve the same API same-origin under /api/journeys/v1, which is how the hosted tenant UI calls it — no CORS configuration required.

Providers: mock and live

Each tenant's journey.yaml selects a provider:

  • mock — the full API surface backed by fixture data and in-memory baskets. Ideal for UI development, demos, and integration tests: identical contracts, no platform dependencies.
  • live — KTAPI and Basket Service, with real inventory, pricing, and Salesforce checkout.

Because both providers implement the same interface, a frontend built against mock works against live without code changes — only journey.yaml changes. See the journey.yaml reference for the live: block.

The neutral /catalog/trips and /catalog/rooms routes are aliases over the established voyage/cabin contract. /catalog/voyages and /catalog/cabins remain available for existing clients.

Third-party ancillary boundary

External services such as insurance or an eSIM do not become Basket Service components. Journeys owns the provider-adapter boundary, tenant-scoped selection, pricing presentation, and checkout write-through. At checkout, platform orchestration persists the selected service as an ad-hoc itinerary item in the same outcome as booking creation; a provider or write failure is explicit and does not leave a silently partial booking. See Extending journeys with third-party services.

Exposed vs exposable

SurfaceWhat it isWhere documented
ExposedThe ~20 endpoints of /api/journeys/v1 — the contract your UI builds against todayJourneys API reference
ExposableThe platform surface behind the wrapper — 16 Basket Service endpoints, the KTAPI commerce groups (packages, items, promotions, itineraries), and Edge Composite for Salesforce orchestrationPlatform API surface

The wrapper exists so tenants get a stable, minimal contract. Today it supports basket construction, payment requests, and new booking creation. Existing-itinerary amendments, cancellation execution, and refunds are not current public routes. Rail search, pre/post options, price alternatives, cancellation quotes, or lifecycle execution become available only when an explicit wrapper route is documented in the API reference and OpenAPI contract.