Skip to main content

Tour Director API

The on-tour surface: a real-time sales dashboard per trip, assisted booking on behalf of guests, cash and card recording, refunds, availability management, goodwill vouchers, and the Tour Director's own settlements — plus the trip-operations endpoints that give the Tour Director their whole operational day (schedule, guest manifest, suppliers, documents, expenses).

All endpoints are under /api/optionals/v1 and require a Tour Director bearer token (see Authentication). Ownership is enforced everywhere: trip-scoped endpoints verify the trip is assigned to the session's Tour Director, and order/transaction endpoints verify the order sits on one of their trips — anything else returns 403 not_your_trip. All amounts are integer minor units.

Authentication

Tour Directors authenticate through Keycloak using the OAuth 2.0 Authorization Code flow with PKCE and MFA, federated to your corporate identity provider (for example Microsoft Entra ID) so they sign in with their existing SSO accounts. The API validates Keycloak-issued access tokens and never accepts staff credentials directly — see Authentication.

On sandbox tenants (provider: mock) only, the evaluation login is:

POST /auth/td
{ "tenantId": "meridian", "code": "TD-4471", "pin": "4471" }

Returns { token, td } (the Tour Director record, without the PIN). The bearer-token transport is identical in both modes.

Trips and the dashboard

My trips

GET /td/trips?tenantId=meridian

Trips assigned to the signed-in Tour Director, each with its resolved brand.

Trip dashboard

GET /td/trips/{tripId}/dashboard?tenantId=meridian

The real-time operational view, in one response:

{
"trip": { "...": "..." },
"brand": { "...": "..." },
"guests": [
{
"guest": { "id": "g_014", "firstName": "Elena", "lastName": "Carter", "party": { "size": 2, "...": "..." } },
"orders": [ { "order": { "...": "..." }, "transactions": [ "..." ], "paymentLabel": "Partially paid", "supplierLabel": "Supplier place pending", "hasPendingOnRequest": true } ],
"totalSpent": 13000,
"outstanding": 13000
}
],
"catalog": [ { "item": { "product": { "...": "..." }, "status": "available", "remaining": 9, "...": "..." }, "sold": 15, "participants": ["Elena Carter", "James Carter"] } ],
"totals": { "grossSales": 412000, "refunds": 8500, "cashCollected": 96000, "cardCollected": 301000, "voucherRedeemed": 15000 },
"gratuityTotal": 54000,
"currency": "GBP",
"dayPlans": [ "..." ]
}
  • guests — every guest party with their orders, payment state labels, and outstanding balances.
  • catalog — per-product sold counts and confirmed participant names, exactly what the Tour Director reads out when making supplier reservations.
  • totals — running trip totals by payment method (cash collected is net of cash refunds).

Selling

Assisted booking

POST /td/trips/{tripId}/orders
{
"tenantId": "meridian",
"guestId": "g_014",
"items": [ { "type": "optional", "productId": "opt_venice_gondola", "quantity": 2, "participants": ["Elena Carter", "James Carter"] } ]
}

Same item shape as the guest order, created on the guest's behalf with channel: td_assisted. When a product is sold out under an on_request policy, an assisted sale may still be taken — the item is flagged onRequestStatus: pending until operations confirms it with the supplier. Returns 201 with the order.

Record a cash payment

POST /td/orders/{orderId}/payments/cash
{ "tenantId": "meridian", "amount": 13000, "payerName": "James Carter" }

Cash is a first-class transaction: it feeds the dashboard totals, settlement, and reconciliation like any card payment. amount optional (defaults to the outstanding balance); payerName records who handed over the cash for split payments.

Take a card payment

POST /td/orders/{orderId}/payments/card

Same body as the guest card payment — used when the Tour Director takes the guest's card on tour. The transaction records the Tour Director as the recorder.

Apply a voucher

POST /td/orders/{orderId}/payments/voucher
{ "tenantId": "meridian", "code": "MER-GV-7Q2K9" }

Accepts either a code or — for codeless goodwill vouchers the Tour Director has just issued — a voucherId.

Pre-check a voucher

POST /td/vouchers/check
{ "tenantId": "meridian", "code": "MER-GV-7Q2K9", "currency": "GBP" }

Same response shape as the guest pre-check; the Tour Director supplies the currency since the sell flow may span trips.

Issue a goodwill voucher

POST /td/vouchers
{
"tenantId": "meridian",
"amount": 5000,
"currency": "GBP",
"expiryDate": "2026-12-31",
"linkedBookingRef": "MT-882401",
"withCode": false,
"notes": "Missed excursion on day 3 - goodwill"
}

An ad-hoc goodwill voucher issued in the field — with withCode: false it is codeless and applied immediately by voucherId; with withCode: true a code is generated for later use. Returns 201 with the voucher.

Corrections

Refund

POST /td/refunds
{ "tenantId": "meridian", "transactionId": "txn_9912", "amount": 8500, "reason": "Guest illness - day 4 excursion" }

Refunds a completed transaction (partial with amount, full without). The refund is linked to the original transaction, restores allotment where applicable, and requires a reason. Tour Directors can only refund transactions on their own trips.

Remove a participant

POST /td/trips/{tripId}/orders/{orderId}/remove-participant
{
"tenantId": "meridian",
"itemId": "item_1",
"participantName": "James Carter",
"reason": "Guest unwell",
"changeRequestId": "evt_2231"
}

Removes one named participant from an optional item and refunds that unit price in one operation. Passing the optional changeRequestId also resolves the guest's change request, clearing it from the overview action list.

Availability

PATCH /td/trips/{tripId}/products/{productId}/availability
{ "tenantId": "meridian", "status": "sold_out", "capacityOverride": 20, "whenSoldOut": "on_request", "note": "Supplier reduced the allotment for this date" }

Sets the per-trip availability override: status (available | sold_out | unavailable), an optional capacityOverride (null clears it), the whenSoldOut policy (block | on_request — preserved from the current setting when omitted), and a note. Returns the stored override. Guests see the effect immediately in the catalog.

Visibility

Sales export (CSV)

GET /td/trips/{tripId}/sales.csv?tenantId=meridian

The Tour Director's own completed sales for the trip as a CSV download (timestamp, order reference, type, method, amount, payer) — their tax and reconciliation record. Scoped strictly to their own transactions; no card data, voucher codes, or finance actions appear.

Trip activity

GET /td/trips/{tripId}/activity?tenantId=meridian

A sanitized, bounded activity feed for the trip — human-readable summaries ("Elena Carter recorded a card payment", "Marco Bellini updated product availability"). Finance settlement actions and other Tour Directors' actions are filtered out.

My settlements

GET /td/settlements?tenantId=meridian

The Tour Director's own settlements — full transparency into what the operator owes them per period and currency, including the administration-fee deduction and status (draftexportedreleased). Read-only; settlement actions belong to finance.

Trip operations

Beyond selling, these endpoints give the Tour Director their operational day in the same app. They follow the same ownership rules.

EndpointWhat it returns / does
GET /td/trips/{tripId}/overview-extrasThe "today" banner, quick-access counts, actionable alerts (including guest change requests), and the crew list
GET /td/trips/{tripId}/scheduleDay-by-day itinerary services with the Tour Director's own status updates applied
PATCH /td/trips/{tripId}/services/{serviceId}/statusUpdate a service's status: pending | in_progress | complete | delayed | cancelled. delayed/cancelled require a reason; reversing a completed or cancelled service requires confirm: true (409 confirmation_required otherwise)
GET /td/trips/{tripId}/manifestGuest duty-of-care manifest: rooms, dietary/medical/accessibility flags, form completion, emergency contacts
POST /td/trips/{tripId}/manifest/{guestId}/remindLog a form-completion reminder for follow-up
POST /td/trips/{tripId}/change-requests/{eventId}/resolveMark a guest change request handled (body: orderId, optional resolution)
GET /td/trips/{tripId}/suppliersSupplier directory for the trip: contacts, booking references, service days
GET /td/trips/{tripId}/documentsCrew documents: rooming list, passenger list, voucher pack, briefing notes, emergency plan
GET /td/trips/{tripId}/expensesCash float, on-ground supplier payment lines, and the Tour Director's own out-of-pocket expenses
POST /td/trips/{tripId}/expenses/payments/{paymentLineId}/settleMark an on-ground supplier payment line settled/unsettled (body: { "settled": true })
POST /td/trips/{tripId}/expensesCapture an out-of-pocket expense: category (Meals, Transport, Tips & Gratuities, Activities, Supplies, Other), description, dayNumber, amount, hasReceipt

Every state change here is audited like the sales endpoints.