URL parameters
Deep links let product pages, marketing emails, and partner integrations drop guests directly into the booking flow with context pre-filled. A guest clicking "Book Now" on a trip page lands on the departure step with the trip already selected — no browsing required.
URL parameters are parsed by the tenant UI (your code in journeys/src/), not by the platform service. The contract below defines the shared parameter names and semantics that all tenant implementations should follow.
Parameter reference
| Parameter | Required | Type | Example | Purpose |
|---|---|---|---|---|
trip | No | string | SOA-2627 | Preselect a trip/package by stable external package code. When valid, skips the trip browse step. |
departure | No | string | ASG099D or 2026-11-18 | Preselect a departure by external departure code or ISO 8601 date. Matched against catalog departure rows after trip is resolved. |
category | No | string | twin-room | Preselect a room/cabin category by tenant slug or exact kaptioName. |
currency | No | string | AUD | ISO 4217 currency code for catalog pricing and basket currency_code. Overrides behavior.currency from journey.yaml. |
adults | No | integer | 2 | Number of adults (1–3). Drives occupancy[].adults on basket creation. |
children | No | integer | 1 | Number of children (0–2). Drives occupancy[].children on basket creation. |
solo | No | 1 | 1 | Shorthand for single occupancy: equivalent to adults=1, children=0 with the catalog model's single-room pricing rules applied. |
All parameters are optional. Omitting them starts at the default first step with platform defaults (USD, 2 adults, 0 children).
Example URLs
# Product page entry — trip preselected, guest lands on departure step
https://book.example.com/?trip=SOA-2627
# Full deep link — trip, departure, currency, and party size pre-filled
https://book.example.com/?trip=SOA-2627&departure=2026-11-18¤cy=AUD&adults=2&children=0
# Solo traveller — single-room rules applied
https://book.example.com/?trip=SOA-2627&departure=2026-11-18&category=balcony-a¤cy=USD&adults=1&solo=1
# Local development with mock provider
http://localhost:<port>/meridian-voyages/?trip=SOA-2627
# Staging with live provider — external codes in URL, resolved via catalog
https://staging.book.example.com/?trip=SOA-2627&departure=ASG099D&category=balcony-a¤cy=USD&adults=2
External codes vs Salesforce IDs
URLs must use external codes that are stable across environments — never Salesforce record IDs.
| Concept | Use in URL | Use in API (after resolution) | Never put in URL |
|---|---|---|---|
| Trip / package | trip = external package code (e.g. SOA-2627) | voyageCode from catalog API | Salesforce package_id |
| Departure | departure = external departure code or ISO date | Resolved via catalog; internal wire format is packageId~departureId | Salesforce departure record ID |
| Room/cabin category | category = slug or kaptioName | categoryKaptioName on GET /catalog/rooms | KTAPI item-option or cabin-type ID |
| Currency | currency = ISO code | currency_code on basket creation | — |
| Party size | adults, children, solo | occupancy: [{ adults, children }] on POST /baskets | — |
The live provider's internal voyageCode wire format (<packageId>~<departureId>) is never suitable for URLs. The tenant UI must call GET /catalog/trips, match external codes from the URL, and pass the resolved voyageCode to downstream catalog and basket calls. /catalog/voyages remains a backward-compatible alias.
Step resolution logic
When URL parameters are present, the tenant UI should walk prerequisites in order and land on the deepest step whose inputs are satisfied:
(no params) → step 0 (trip browse)
trip present → at least step 1 (departure & travellers)
+ departure valid → step 1 with departure pre-filled
+ adults/children → step 1 state fully seeded
+ category valid → at least step 2 (room/cabin category pre-selected)
If a parameter value is invalid or not found in the catalog, fall back gracefully to the earliest step — never show a hard error page.
Channel selection
Channel selection (b2c-direct vs b2b-advisor) and advisor identification run before the step flow when journey.yaml defines multiple channels or requires_advisor_id: true. URL parameters do not bypass channel selection.
Implementation pattern
Parse parameters once on mount, resolve against the catalog, and seed React state:
import { useState, useEffect } from 'react';
function useDeepLinkParams(voyages) {
const params = new URLSearchParams(window.location.search);
const tripCode = params.get('trip');
const departureCode = params.get('departure');
const categorySlug = params.get('category');
const currency = params.get('currency');
const adults = parseInt(params.get('adults') || '2', 10);
const children = parseInt(params.get('children') || '0', 10);
const solo = params.get('solo') === '1';
const matchedVoyage = voyages.find(v => v.voyageCode === tripCode);
return {
initialStep: matchedVoyage ? 1 : 0,
voyage: matchedVoyage || null,
departureCode,
categorySlug,
currency: currency || 'USD',
occupancy: {
adults: solo ? 1 : Math.min(Math.max(adults, 1), 3),
children: solo ? 0 : Math.min(Math.max(children, 0), 2),
},
isSolo: solo,
};
}
Verification checklist
When implementing or modifying URL parameter handling, verify:
- No parameters → default entry step (step 0 without
trip) -
tripalone → step 1, trip row selected, catalog loads for chosen currency - Invalid / unknown codes → graceful fallback to earliest step (no error page)
-
categoryaccepts both a tenant slug and exactkaptioNamefor the configured room/cabin model -
adults/children/soloreflected in departure step state and inPOST /basketsoccupancy - Live provider: URL never contains
~-separated Salesforce IDs -
currencyoverrides thebehavior.currencydefault fromjourney.yaml - Parameters are not re-read on navigation within the flow (parse once on mount)