Mental model
The single most important thing to get right is which surface you are calling. Your deployment exposes two of them:
The base URL is your deployment’s own domain, for example
https://operator.example.com, not api.voyant.travel. The hosted
Cloud, Connect, and Data
SDKs in the SDKs tab are the opposite case: they target api.voyant.travel with
a bearer key. The storefront and public surface described here run on your app
and need no key. See the note in
API routes.
That split maps to two clients:
@voyant-travel/storefront-sdk
The public client. Safe to ship to a browser or mobile bundle. Carries no
secret. Talks to
/v1/public/*.@voyant-travel/admin-react
The admin client. Server only. Carries a
voy_ API key. Talks to
/v1/admin/*. Never embed it in a client bundle.Browse and book from a TypeScript app
Install the public SDK and point it at your deployment domain. There is no key to pass.storefront (catalog reads),
booking (the raw session lifecycle), bookingEngine (the recommended,
flow-oriented wrapper), and checkout (payment collection).
1
Browse the catalog
Read settings, then departures, availability, and pricing for a product. All
of these are plain reads on the Marketing-style offers are also available with
storefront facade.voyant.storefront.listProductOffers(productId) and
voyant.storefront.getOfferBySlug(slug).2
Reserve a booking session
Prefer the When you already have a selected departure and quote and want the combined
payload in one call (session, availability, repricing, payment plan,
allocation, and the checkout capability attached at
bookingEngine facade. reserve opens a public booking session
and returns a canonical snapshot whose engine.state you can gate UI on.session.checkoutCapability), use voyant.booking.bootstrapSession(...)
instead. An async bootstrap returns 428 if you do not supply an
idempotency key; see the next step.3
Follow the server's next action
Do not derive engine state on the client. The server publishes what to do
next, and a host renders it rather than inventing it.Recoverable errors come back in an envelope carrying Map each value to a recovery affordance in your UI. Because the set is
closed and server-owned, what you render and what the server enforces stay
one derivation instead of two that drift.
code, message,
recoverable, and nextAction. nextAction is drawn from a closed set
(bookingEngineNextActions):4
Collect travelers and reprice
Update travelers and reprice as the customer fills the form. Pass an
idempotency key on writes so retries do not double-apply.Every write method accepts a trailing
StorefrontRequestOptions with
headers and idempotencyKey. The key is sent as the Idempotency-Key
header.5
Take payment and confirm
Drive payment through the engine, gating each step on state, then confirm.Re-read the snapshot any time with
voyant.bookingEngine.getSnapshot(sessionId). Lower-level payment helpers
(previewPayment, bootstrapPayment) and the checkout facade
(previewCollection, initiateCollection, bootstrapCollection) are there
when you need finer control.Errors
Failed calls throwVoyantStorefrontApiError with status, body, and a
normalizedError (the parsed { code, message, details } envelope). Branch on
normalizedError.code, not on the message string.
Build a React website
For a React site, layer the hooks packages on top of the same public contract.@voyant-travel/storefront-react covers the storefront and booking flows;
@voyant-travel/catalog-react adds catalog search and a booking-engine hook
family.
QueryClientProvider (v5 is a peer dependency).
{ baseUrl, fetcher?, children }. Inside the tree, read with
hooks:
@voyant-travel/catalog-react. It exposes VoyantCatalogProvider (same
{ baseUrl, fetcher } context, so it composes with the storefront provider),
discovery hooks useCatalogSearch, usePackageSearch, usePackageDetail,
useCatalogSlots, and, under @voyant-travel/catalog-react/booking-engine, the
booking hooks useBookingDraft, useBookingQuote, and useBookingCommit.
These hooks can target /v1/admin or /v1/public depending on the baseUrl
and the route they call.
Mobile or any other language: raw REST
The SDK is a convenience, not a requirement. Everything is plain HTTP and JSON under/v1/public/*, so a native mobile app or a backend in any language can
call it directly.
Routes. Public routes are organized by business capability, not by the
frontend that calls them: /v1/public/bookings, /v1/public/products,
/v1/public/pricing, and so on. The full set is documented in the
Storefront API reference in the Platform navigation, with your app domain
as the server URL.
Success envelope. Reads return { "data": ... }. List endpoints add
pagination alongside data.
code,
not on error.
requestId is also returned in the X-Request-Id response header on success
and failure alike. Keep it in logs and support requests.
Idempotency. Writes accept an Idempotency-Key request header so a retry
does not create a duplicate. Async bootstrap endpoints reject a write with
428 if the key is missing.
CORS_ALLOWLIST env decides which origins get credentialed CORS headers: list
exact origins or a single-label wildcard such as https://*.example.com. A bare
* is rejected for credentialed requests. Native mobile apps do not run a
browser CORS preflight, so the allowlist does not affect them.
Caching. GET /v1/public/* responses that the route marks
Cache-Control: public, s-maxage=... and that carry no Set-Cookie are served
from a shared cache. To force a fresh read, send Cache-Control: no-cache on
your request.
Read admin data from your own server
When you need data only the admin surface exposes (full bookings, finance, staff operations), call it from your backend with@voyant-travel/admin-react and a
voy_ API key. This client is server only. Never ship it or the key to a
browser or mobile bundle; a public site reaches admin data by proxying through
its own backend.
AdminApiError carrying status, code, and
requestId. Use admin.capabilities() to discover which modules and operations
a given deployment enables. Mint and scope voy_ keys per
Authentication, and grant each key only the scopes it
uses.
A working example
There is no standalone example app to clone. The living reference is the operator application’s storefront route group atstarters/operator/src/routes/(storefront)/, which consumes these same public
contracts, plus the
@voyant-travel/storefront-sdk README.
Next steps
Public API
The customer-facing surface, its packages, and where checkout fits.
API routes
The admin/public split, the error envelope, and the route conventions.
SDKs overview
The hosted Cloud, Connect, and Data clients and their shared shape.
SDK authentication
How
voy_ API keys, scopes, and server-only token rules work.