Skip to main content
This guide is about consuming the API of a Voyant deployment you (or your customer) run, from your own website, mobile app, or backend. The deployment exposes the catalog browse, pricing, and booking flows under a public surface, and you drive them from outside.

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.
If your website needs admin data, your own backend calls the admin client and proxies the result. The API key stays on your server, per Authentication.

Browse and book from a TypeScript app

Install the public SDK and point it at your deployment domain. There is no key to pass.
The client groups operations into four facades: 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 storefront facade.
Marketing-style offers are also available with voyant.storefront.listProductOffers(productId) and voyant.storefront.getOfferBySlug(slug).
2

Reserve a booking session

Prefer the bookingEngine facade. reserve opens a public booking session and returns a canonical snapshot whose engine.state you can gate UI on.
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 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 code, message, recoverable, and nextAction. nextAction is drawn from a closed set (bookingEngineNextActions):
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.
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 throw VoyantStorefrontApiError with status, body, and a normalizedError (the parsed { code, message, details } envelope). Branch on normalizedError.code, not on the message string.
There is no checkout login. The checkout surface is protected by a short-lived, server-issued checkout-capability cookie attached to the session, not a user account.

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.
Both providers re-export one shared context, so you wrap your app once with a provider and a TanStack Query QueryClientProvider (v5 is a peer dependency).
The provider takes { baseUrl, fetcher?, children }. Inside the tree, read with hooks:
For dynamic catalog browse and a hook-based booking flow, add @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.
Error envelope. Errors return one shape on every route. Branch on 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. Browser callers must be allowlisted. The deployment’s 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.
Non-2xx responses throw 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 at starters/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.