Skip to main content

Dataset contracts — config-based API slices

The standard Journeys API is a general-purpose surface: catalog, baskets, pricing, payment, and booking, designed for building storefronts, advisor portals, and conversational experiences. Some integrations need something different — a partner already has a schema, and wants your catalog and pricing delivered in their wire shape rather than adapting their systems to yours.

Dataset contracts solve this without a second booking stack. A dataset is a configured, read-optimized slice of the same commerce engine, declared in journey.yaml and served as versioned HTTP resources. The platform maps its canonical catalog and pricing data into the partner's schema through a named format adapter — configuration selects the format; no tenant-specific code is written or forked.

Same engine, two surfaces:

Kaptio commerce platform (catalog, pricing, inventory)

├── Journeys API /api/journeys/v1/* ← storefronts, portals, AI agents

└── Datasets /api/journeys/v1/datasets ← partner feeds in the partner's schema

Configuration

A dataset is declared per journey in journey.yaml:

datasets:
- id: partner-feed
format: ndc-offerprice-v1
partition_by: package
schema_file: schemas/partner/packagesearch_request.schema.json
KeyMeaning
idThe dataset's URL segment: /datasets/{id}.
formatA registered format adapter — the mapping from platform data to the partner's wire shape (for example ndc-offerprice-v1, targeting an NDC 21.3 OfferPrice response). Unknown format ids fail configuration load, not requests.
partition_byHow the dataset splits into independently fetchable partitions. package produces one partition per package.
schema_fileA JSON Schema in your configuration repository, served back to the partner from the dataset itself so both sides validate against the same contract.

The HTTP surface

GET /api/journeys/v1/datasets/{datasetId} — index: which partitions exist, when they were built
GET /api/journeys/v1/datasets/{datasetId}/packages/{code} — one rendered partition, with an ETag
GET /api/journeys/v1/datasets/{datasetId}/bulk — every partition merged into one document, with a dataset-level ETag
GET /api/journeys/v1/datasets/{datasetId}/schema — the response JSON Schema (?type=request serves the request schema)
POST /api/journeys/v1/datasets/{datasetId}/quote — a real-time price quote in the partner's shape

Every dataset route requires an x-api-key header. Keys are per tenant, issued by Kaptio, and delivered through a secure channel — dataset routes carry commercial data, so they are authenticated from day one rather than using the session model of the hosted booking UI.

Request and response shapes are defined by the dataset's own schemas: GET .../schema serves the exact JSON Schema the partitions and quote responses validate against, and GET .../schema?type=request serves the quote request contract, so both sides build from the same documents. The quote endpoint accepts the partner's own request formats (including tolerant, envelope-style shapes with correlation-id echo) alongside the platform's strict contract — configuration decides which schemas apply, no code fork.

Multi-currency: a dataset is warmed in every currency its configuration lists; each partition carries per-currency price entries side by side, and quotes price in any requested currency (natively priced or derived through the tenant org's currency conversion setup).

Bulk retrieval: GET .../bulk returns the whole dataset as one document — every package's offers and price lists merged, with a dataset-level ETag so a feed consumer polls one URL and re-downloads only on change. The document is bounded by design: a dataset serves the packages its configuration scopes (typically tens of products), not an operator's full catalog, so the payload stays in single-digit megabytes. Catalog-scale distribution is a different integration pattern (delta synchronisation) agreed with Kaptio separately — the bulk route intentionally does not paginate. All routes return a structured JSON error body on failure:

StatusMeaning
401Missing or wrong x-api-key for the tenant.
404Unknown dataset id, or a partition that is not (yet) in the warm store — see the freshness model below.
400Quote validation failure (for example a missing service_level_id), with the failing field named in the error body.
429Quote rate limit exceeded for the tenant. Back off and retry.
304Not an error — the conditional partition request matched the ETag; reuse your cached copy.

Freshness and caching

Partitions are not rendered per request from live upstream calls. A warm engine rebuilds each package's source document on a rolling cycle, and the format adapter's rendering is memoized by content hash — unchanged content is never re-rendered and never re-downloaded:

  • Poll the index, don't hardcode partitions. The index lists exactly which partitions are currently available and when each was built.
  • Use If-None-Match. Every partition response carries an ETag; a conditional request returns 304 Not Modified when nothing changed.
  • Expect a short cold window after platform deploys. The warm store rebuilds from scratch; partitions reappear on the index as they complete.

Quote responses are the bare partner document — the body validates against the published response schema exactly as served, with pricing provenance in the X-Price-Source response header (warmed when served from the rebuilt store, live when priced in real time).

Quotes take the fastest honest path: when the requested currency and occupancy match the basis the dataset was last rebuilt with, the quote serves from that store (X-Price-Source: warmed — the same price the partition shows, answered in milliseconds); any other combination prices in real time against the platform (X-Price-Source: live). Occupancy, service level, and date are honored on every call either way, and the result is returned in the partner's schema. Quote routes are rate-limited per tenant.

When to use which surface

Standard Journeys APIDataset contract
SchemaKaptio's published contractThe partner's schema
Best forBuilding UIs: storefronts, advisor portals, chat and AI-agent experiencesFeeding an existing partner system that already speaks its own format
WritesBaskets, payments, bookingsRead-only, plus real-time quotes
AuthTenant session modelPer-tenant x-api-key
FreshnessLive per request (with platform caching)Rolling warm rebuilds + ETag; quotes warmed-or-live (X-Price-Source header)

The two surfaces share the same underlying catalog, pricing, and inventory — there is one engine underneath. They can differ temporarily: dataset partitions are snapshots rebuilt on a rolling cycle, while the standard API prices per request, so a change in the platform reaches the standard API first and the dataset on its next rebuild. Quotes on the dataset surface either match the latest rebuild (warmed) or price in real time (live) — the X-Price-Source header says which.

Adding a new format

Format adapters are platform code, versioned and validated (a format ships with fixture-based tests against the partner's schema). If a partner's schema is not yet in the registry, Kaptio adds the adapter once and any tenant can then select it in configuration. Talk to your Kaptio contact about lead times for a new format.

For endpoint definitions of the standard surface, see the Journeys API reference; for how the warm engine and caching behave under load, see Performance.