API and Ontology Reference

The operational ontology, as an API.

UnityTrip is built as a set of deterministic services over a shared operational ontology. This reference describes that model — passenger types, trip reasons, priorities, quotas, penalties — and the endpoints that read and enforce it: the policy engine, booking, approvals, payments consistency, and the access model.

The API described here is the platform's internal service surface — provisioned per tenant, role-based, and audited — rather than a public self-serve API. It is documented because the model, not the transport, is the interesting part: it is the same ontology described in the glossary and the policy engine, made concrete. Machine-readable versions: policy-engine · booking · user-manager · approvals (sanitised OpenAPI, servers and authentication omitted) · leased-travel-policy JSON Schema — the document the Policy Builder produces.

The ontology is the data model

Most travel systems model a trip and hang a flight and a hotel off it. UnityTrip models the operation. Passenger types, trip reasons, transportation modes, priorities, quotas, and penalties are first-class entities that the booking, policy, and approvals services all read from. A booking decision is a projection of that model, not a hard-coded workflow — which is why a new rule is usually a change to data, not to code.

EntityWhat it represents
PassengerTypeA class of traveller — staff, contractor, family, guest — with its own booking source (Corporate, Private, Any) and priority. Sub-types refine it further.
TripReasonWhy the journey is taken. Reasons carry their own quotas and entitlements, so business, training, and compassionate travel are governed differently.
TransportationModeA first-class enumeration: Accommodation, Air, Boat, Maritime, Vehicle, Road — charter aircraft, chartered vessels, ground fleets, and guesthouse rooms share one segment model.
PassengerPrioritiesMaps a passenger type to a numeric priority under a given rule, so competing requests for the same seat are ranked consistently.
Quota · TimeFrameA cap on seats per employee, per trip reason, per passenger type, per year — within a time frame of PerAnnum, PerWeek, PerSector, PerPeakSail, or PerOffPeakSail. The mechanism that rations a fareless seat, on aircraft and vessels alike.
BookingEventA behavioural fact recorded against a booker: Cancellation, Noshow, Reschedule, Other. Penalty points are computed from this history, scaled to departure proximity.
SegmentOne leg of a journey: a transportation mode, a departure date, a peak-period flag, and a release time that gates when it may be booked.
FareCategoryThe class of service or entitlement tier applied to a booking.
PolicyGroup · DivisionOrganisational placement of a traveller — division, department, unit, employee level — that policy and approval routing key off.

Policy engine

The policy engine is the heart of the platform. It takes an itinerary and the passengers on it and returns a deterministic decision against every booking, penalty, and quota rule — with the failed reasons named, so a refusal can always be explained.

EndpointPurpose
POST /rules/validateValidate an itinerary against all booking, penalty, and quota rules.
POST /rules/validator/penaltiesEvaluate a booker's penalty points from their history of booking events.
POST /rules/validator/searchBasic rule validation for a lookup request.
POST /rules/segment/validateCheck a request against each segment's release time.
GET /rules/resultRetrieve the outcome of a prior validation.
PUT /rules/quota/adjustSet or adjust the seat quota for an employee, scoped to trip reason, passenger type, year, and time frame.
GET /settings/passengerTypesThe defined passenger types, each with its booking source and ad-hoc flag.
GET /settings/tripReasonsThe defined trip reasons that quotas and entitlements attach to.

Validate a booking

POST /rules/validate

{
  "outbound": {
    "segments": [
      {
        "transportationMode": "Air",
        "departureDate": "2026-08-14T07:00:00Z",
        "isPeakPeriod": false
      }
    ],
    "passengerTypeIds": ["staff", "contractor"]
  },
  "return": {
    "segments": [
      { "transportationMode": "Air", "departureDate": "2026-08-28T07:00:00Z" }
    ],
    "passengerTypeIds": ["staff", "contractor"]
  }
}

// response
{
  "passed": false,
  "failedReasons": ["QUOTA_EXCEEDED: contractor, trip reason TRAINING, 2026"],
  "outbound": { "passed": true },
  "return":   { "passed": false }
}

The response is symmetric to the request — a result per direction — and the failed reasons are machine-readable strings naming the exact rule that fired. The same inputs return the same result every time.

Evaluate penalties

POST /rules/validator/penalties

{
  "bookerId": "E-10293",
  "bookingEvents": [
    { "type": "Noshow",       "occurredAt": "2026-06-02T05:40:00Z" },
    { "type": "Cancellation", "occurredAt": "2026-06-19T21:15:00Z" }
  ],
  "travelers": ["E-10293"]
}

A no-show hours before departure carries more points than an early cancellation; accumulated points can trigger a temporary ban that still allows a colleague to book on the offender's behalf.

Adjust a quota

PUT /rules/quota/adjust

{
  "employeeId": "E-10293",
  "tripReasonId": "training",
  "passengerTypeId": "staff",
  "year": 2026,
  "quotaSeats": 12,
  "timeFrame": "PerAnnum"
}

Booking

The booking service turns a validated request into a held and confirmed journey across charter and commercial legs — with traveller profiles, fare categories, land transport, and asset utilisation in the same surface.

EndpointPurpose
GET /trips/searchThe possible connections for a request across owned, leased, and commercial content.
GET /trips/locationsAll active locations in the network.
POST /trips/booking/start-sessionOpen a booking session scoped to trip reasons, destinations, transport modes, passenger types, fare categories, and traveller limits — the policy-shaped frame a booking is made inside.
POST /trips/bookCreate a booking for one or more travellers, each carrying an outbound and return priority, against a cost activity and an external reference.
POST /trips/booking/confirm/{reference}Confirm a held booking through the consistency check.
DELETE /trips/booking/{reference}Cancel a booking — recorded as a booking event against the booker.
GET /trips/check-consistencyVerify that bookings are consistent with segment entries.
POST /trips/booking/transport/eventsAttach a land transport event to a booking — the road leg on the same record.
POST /trips/booking/{ref}/additionalFieldsUpsert client-specific fields on a booking; remove per field key.
POST /trips/booking/{ref}/external-identifierSet an external identifier, tying the booking to an upstream system.
GET /trips/historyThe booking history for a booker.
GET /farecategories/listAll fare categories.
GET /travelers/profile/{idOrEmail}The travel profile of an employee — with search, bulk import, and enable/disable endpoints alongside.
GET /bookers/profilesAll booker profiles — a booker is an independent traveller with an employee id.
GET /booker/{employeeId}/eventsThe booking activity of a booker — the event history penalties are computed from.
POST /asset/utilizationRecord and query seat utilisation for owned and leased assets, by asset and by date — the figure that turns empty capacity into recovered cost.
GET /expenses/export · POST /expenses/importMove expense-relevant data to and from the finance side.
POST /elina/update-bookingSynchronise a booking with Elina PMS, so the guesthouse room lives on the same record as the seat.

Create a booking

POST /trips/book

{
  "outboundTrip": "TRIP-0814-AM",
  "returnTrip": "TRIP-0828-PM",
  "bookerEmployeeId": "E-10293",
  "externalReference": "PO-448812",
  "activity": "ACT-TRAINING-2026",
  "sessionId": "3f7c9a4e",
  "travelers": [
    {
      "firstname": "Jordan",
      "lastname": "Reid",
      "type": "staff",
      "employeeId": "E-10293",
      "outboundPriority": 2,
      "returnPriority": 2
    }
  ]
}

Every booking carries a cost activity and an external reference from the moment it is created — the finance linkage is not an afterthought at reconciliation.


Payments and consistency

This is the machinery behind the self-correcting payment broker. A booking confirms through a per-segment consistency check, and the payment gateway's outcome is acknowledged against the booking session — so a race on the last seat, or a payment that did not complete, resolves cleanly instead of becoming a reconciliation case.

POST /trips/pay/{bookingReference}

POST /payments/acknowledge
{
  "reference": "BKG-7Q2M4",
  "wasPaid": true,
  "sessionId": "3f7c9a4e",
  "transactionDetails": { "gateway": "stripe", "transactionId": "tx_9f27d3" }
}

// consistency result, per segment and direction
{
  "bookingId": "b6f0c2e1",
  "reference": "BKG-7Q2M4",
  "outboundResults": [ { "error": "None" } ],
  "returnResult":   [ { "error": "None" } ]
}

Different bookers can use different gateways — the acknowledgement model is gateway-agnostic, which is what makes multi-gateway resilience possible.


Approvals

Approvals are part of the record, not a separate email thread. The approver service returns the open items for a given approver, each tied to a claim and a booking, with a deadline. Claim lifecycle events are pushed as webhooks — see the claim approvals webhook reference.

GET /approvals/E-30417

[
  {
    "claimNumber": "CLM-2026-08841",
    "bookingReference": "BKG-7Q2M4",
    "status": "PendingApproval",
    "sequenceNumber": 1,
    "createdAt": "2026-08-01T09:12:00Z",
    "approvalDeadline": "2026-08-03T09:12:00Z",
    "requestTitle": "Rotation travel, August",
    "requester": {
      "firstname": "Jordan",
      "lastname": "Reid",
      "employeeId": "E-10293",
      "employeeLevel": "L4",
      "division": "Operations",
      "department": "Field Services",
      "unit": "Rotations",
      "passengerType": "Staff",
      "policyGroupId": "PG-OPS"
    }
  }
]
FieldMeaning
claimNumberThe claim the approval belongs to. For business bookings, no payment executes without an approved claim number.
bookingReferenceThe booking the claim relates to.
status · sequenceNumberThe lifecycle state of the claim and this approver's position in the chain — approvals route in sequence under delegation of financial authority.
approvalDeadline · approvalTimeLimitWhen the approval window closes, so time-critical travel is not stranded in a queue.
requesterThe traveller's full organisational placement — division, department, unit, employee level, passenger type, policy group — the coordinates policy and routing key off.

Access, identity, and audit

Every service is governed the same way, and this is the API face of per-identity observability: access is role-based, activity is attributable to an individual identity, and the mapping of who may do what is itself a first-class, queryable object.

EndpointPurpose
POST /securitycenter/manifestThe security manifest: a declarative map of an application's operations to the roles permitted to perform them, including modifiers and dependencies between permissions.
GET /securitycenter/activitiesPer-identity activity — the observability that lets a rogue automation be isolated down to the individual account.
GET /securitycenter/appsThe applications registered under the security model.
POST /roles/add · POST /roles/removeGrant and revoke roles.
POST /self-register/add · addbulk · DELETE removeTraveller self-registration, singly or in bulk — onboarding a rotation without a helpdesk queue.

Each client is an isolated tenant. Every booking, change, approval, and claim is recorded as an immutable event, so the state of the system is always reconstructable and every decision is attributable. No language model decides a booking at run time — the doctrine is frontier AI at build time, determinism at run time. These interfaces are hardened by production use: idempotent booking, self-correcting payment reconciliation, and an event-sourced core designed to absorb disruption-day load surges. Details in the architecture and security references.


Common questions

What is an operational ontology in a travel platform?

An operational ontology is a structured, machine-readable model of an operation — its passenger types, trip reasons, transportation modes, entitlements, quotas, priorities, and rules — that a policy engine can execute directly. In UnityTrip the ontology is the live data model, not documentation: booking, policy, and approvals services all read from it, so a booking decision is a projection of the ontology rather than hard-coded workflow.

How does UnityTrip's policy engine validate a booking?

A single call submits the itinerary — outbound and return trips, each a set of segments with a transportation mode and departure date — together with the passenger types involved. The engine evaluates it against every booking, penalty, and quota rule and returns a deterministic pass or fail with the specific failed reasons named. The same request returns the same result every time, which is what makes decisions auditable and reproducible.

Does UnityTrip have an API for quota management?

Yes. Quotas are scoped per employee, per trip reason, per passenger type, per year, within a time frame — annual, weekly, per sector, or per peak and off-peak sailing — and a dedicated endpoint adjusts them. The same quota model is what the validation endpoint enforces at booking time.

How are no-shows and penalties handled through the API?

Booking events — cancellation, no-show, reschedule — are recorded against the booker, and a penalties endpoint evaluates the accumulated points from that history, scaled to how close to departure each event occurred. Segment release times are validated separately, gating who may book which leg and when.

Which transportation modes does the model support?

The transportation mode is a first-class enumeration covering accommodation, air, boat, maritime, vehicle, and road — so charter aircraft, chartered vessels, ground fleets, and guesthouse rooms share one segment model, one policy layer, and one booking record.

Can approvals be integrated through the API?

Yes. The approver service returns all open approvals for a given approver, each carrying the claim number, booking reference, status, an approval deadline, and the requester's organisational placement — division, department, unit, employee level, passenger type. Webhook events for the claim lifecycle are documented in the claim approvals webhook reference.

How does the API prevent overbooking and payment mismatches?

Bookings confirm through a consistency check that reconciles the result per segment, so a race on the last seat resolves cleanly rather than overbooking, and a payment acknowledgement endpoint records the gateway outcome against the booking session — the machinery behind the self-correcting payment broker described in the architecture.

Is the UnityTrip API deterministic and secured?

Yes. The services are deterministic by design — no language model decides a booking at run time — and every action is recorded as an event, giving a complete audit trail. Access is role-based over isolated tenants, governed by a security manifest that maps each application's operations to permitted roles. The API described here is the platform's internal service surface, provisioned per tenant under RBAC rather than a public open API.

Integrate With UnityTrip

The ontology is yours to configure; the engine that executes it stays deterministic and auditable.

Build a policy Talk to us