Skip to main content

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

ParameterRequiredTypeExamplePurpose
tripNostringSOA-2627Preselect a trip/package by stable external package code. When valid, skips the trip browse step.
departureNostringASG099D or 2026-11-18Preselect a departure by external departure code or ISO 8601 date. Matched against catalog departure rows after trip is resolved.
categoryNostringtwin-roomPreselect a room/cabin category by tenant slug or exact kaptioName.
currencyNostringAUDISO 4217 currency code for catalog pricing and basket currency_code. Overrides behavior.currency from journey.yaml.
adultsNointeger2Number of adults (1–3). Drives occupancy[].adults on basket creation.
childrenNointeger1Number of children (0–2). Drives occupancy[].children on basket creation.
soloNo11Shorthand 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&currency=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&currency=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&currency=USD&adults=2

External codes vs Salesforce IDs

URLs must use external codes that are stable across environments — never Salesforce record IDs.

ConceptUse in URLUse in API (after resolution)Never put in URL
Trip / packagetrip = external package code (e.g. SOA-2627)voyageCode from catalog APISalesforce package_id
Departuredeparture = external departure code or ISO dateResolved via catalog; internal wire format is packageId~departureIdSalesforce departure record ID
Room/cabin categorycategory = slug or kaptioNamecategoryKaptioName on GET /catalog/roomsKTAPI item-option or cabin-type ID
Currencycurrency = ISO codecurrency_code on basket creation
Party sizeadults, children, solooccupancy: [{ 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)
  • trip alone → step 1, trip row selected, catalog loads for chosen currency
  • Invalid / unknown codes → graceful fallback to earliest step (no error page)
  • category accepts both a tenant slug and exact kaptioName for the configured room/cabin model
  • adults / children / solo reflected in departure step state and in POST /baskets occupancy
  • Live provider: URL never contains ~-separated Salesforce IDs
  • currency overrides the behavior.currency default from journey.yaml
  • Parameters are not re-read on navigation within the flow (parse once on mount)