Skip to main content

Experiences API overview

Every business capability in Kaptio Experiences is a documented REST/JSON endpoint — the three tenant apps are pure consumers of this API, so anything the UI can do, an integration can do. The surface is organized by persona:

  • Guest API — trip context, catalog, orders, payments, vouchers
  • Tour Director API — dashboard, assisted sales, cash, refunds, availability, trip operations
  • Operations & Finance API — oversight, vouchers, settlements, finance export, reporting, audit

Base path and versioning

/api/optionals/v1

The version lives in the path. Within a version, changes are additive only — new endpoints, new optional fields, new enum values documented as they appear. A breaking change means a new version, with the previous version supported through a communicated migration window.

Tenant resolution

The API is multi-tenant. Every request identifies its tenant with a tenantId — as a query parameter on GET requests, or as a body field on POST/PATCH requests where noted:

GET /api/optionals/v1/config?tenantId=meridian

An unknown tenant returns 404 tenant_not_found; a missing tenant returns 400 tenant_required.

Public tenant config

One endpoint is public (no authentication) — it serves the UI shell its brands, themes, and feature flags, and is safe to call from anywhere:

GET /config?tenantId=meridian
{
"tenantId": "meridian",
"productId": "td-optionals",
"provider": "mock",
"currencies": ["GBP", "USD", "EUR"],
"brands": [ { "id": "meridian_uk", "name": "Meridian Travel UK", "theme": { "...": "..." } } ],
"features": { "gratuities": true, "vouchers": true, "cash_payments": true, "split_payments": true, "pre_trip_purchase": true },
"payment": { "gateway": "simulated" },
"ui": { "title": "Meridian Experiences" }
}

It never includes secrets or integration settings.

Authentication

Access is role-scoped, and every authenticated request carries a short-lived bearer token:

Authorization: Bearer <token>
ConsumerHow they authenticate
GuestsGuests never register. They reach their trip-scoped session either through a magic link delivered by email (an opaque single-use code exchanged server-side for the session — the token never appears in the URL; see how magic links work) or by booking reference + surname via POST /auth/guest, matched against the booking records for the trip. Both issue the same short-lived, trip-scoped token (12-hour lifetime — a full touring day). Which entry points are active is agreed per tenant.
Tour Directors, Operations & FinanceKeycloak OIDC. Browser clients use the OAuth 2.0 Authorization Code flow with PKCE, with MFA enforced by policy. Keycloak federates to your corporate identity provider (for example Microsoft Entra ID), so staff sign in with their existing SSO accounts. The API validates Keycloak-issued access tokens — it never accepts or stores staff passwords. Issuer, audience, and role mappings for your realm are provisioned at onboarding.
Programmatic consumersOAuth 2.0 client credentials against Keycloak for server-to-server integrations (for example an integration-platform counterparty), over TLS 1.2+.

Sessions carry the role, tenant, and subject; guest sessions are additionally trip-scoped — a guest token can never read or pay against another party's orders, enforced server-side on every route. Role-based authorization gates every endpoint: a Tour Director token cannot call admin endpoints, and a Tour Director can only operate trips assigned to them (403 not_your_trip otherwise).

GET /me returns the current session's claims — useful for session restoration in integrations and UIs.

Evaluation sandbox logins

Tenants running provider: mock (evaluation and training) replace the Keycloak staff flows with sandbox credential logins so a walkthrough needs no identity setup: POST /auth/td (Tour Director code + pin) and POST /auth/admin (email + password). These endpoints exist only on sandbox tenants and are not part of the production contract. Booking reference + surname guest sign-in works in both modes; the magic-link entry point is provisioned for live tenants.

Collections and pagination

Most collections are naturally bounded: guest and Tour Director endpoints are scoped to a single trip or person, and reports and settlement runs take explicit from/to periods. For the tenant-wide admin collections (trips, vouchers, settlements, the audit trail), scope requests with the documented filters; the audit trail additionally accepts a limit. A uniform pagination convention — limit plus an opaque continuation cursor, with a server-side maximum page size — is being introduced across the tenant-wide collections as an additive change within v1, so treat collection responses as pageable when building integrations. CSV exports are bounded by the same filters and periods.

Conventions

ConventionDetail
MoneyAll monetary amounts are integers in minor units (pence/cents) of the trip currency. 12500 with currency GBP is £125.00. Refunds and chargebacks are stored as positive amounts with the transaction type set accordingly.
Dates and timesDates are ISO YYYY-MM-DD; timestamps are ISO 8601 UTC (2026-07-12T08:45:00.000Z).
IdentifiersOpaque strings. Human references exist alongside ids where people need them — order references like OPT-260712-8341, settlement references like SET-2026W27-TD4471-EUR.
CSV exportsEvery report endpoint also serves CSV: append ?format=csv and the response switches to text/csv with a download disposition. Tour Director sales export is CSV-native.
ValidationRequest bodies are schema-validated. Invalid input returns 400 with code: validation_error and a human-readable message for the first failing field.

Error shape

Errors are uniform across the whole surface:

{ "error": "Units are not available for this experience", "code": "unavailable" }
StatusCodes you will seeMeaning
400validation_error, tenant_required, reason_requiredMalformed or incomplete request
401unauthorized, booking_not_found, invalid_credentialsNo/expired token, or login failed (login errors are deliberately uniform — they never reveal which credential field was wrong)
402payment_failed, gateway decline codesPayment was attempted and declined
403not_your_tripAuthenticated, but the resource belongs to another Tour Director's trip
404not_found, tenant_not_found, trip_not_foundResource does not exist (or is not visible to this session)
409unavailable, confirmation_requiredAllotment/cut-off conflict, or a destructive change needs an explicit confirm: true
422voucher validation codes (voucher_expired, voucher_redeemed, …)The request was understood but the business rule rejected it
500internal_errorUnexpected failure — logged with request context, never with payload details

Operability

The service exposes the platform-standard endpoints outside the API path: /health (liveness), /ready (readiness), and /metrics (Prometheus — request counts, durations, and error rates per endpoint). Rate limits and quotas for programmatic consumers are agreed per integration contract and enforced with the platform's standard middleware.

Audit

Every state-changing call — logins, orders, payments, refunds, availability changes, voucher actions, settlement actions — writes an audit event with actor type, actor id, action, entity, and timestamp. The trail is queryable via the admin audit endpoint.