API and Ontology Reference
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.
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.
| Entity | What it represents |
|---|---|
PassengerType | A class of traveller — staff, contractor, family, guest — with its own booking source (Corporate, Private, Any) and priority. Sub-types refine it further. |
TripReason | Why the journey is taken. Reasons carry their own quotas and entitlements, so business, training, and compassionate travel are governed differently. |
TransportationMode | A first-class enumeration: Accommodation, Air, Boat, Maritime, Vehicle, Road — charter aircraft, chartered vessels, ground fleets, and guesthouse rooms share one segment model. |
PassengerPriorities | Maps a passenger type to a numeric priority under a given rule, so competing requests for the same seat are ranked consistently. |
Quota · TimeFrame | A 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. |
BookingEvent | A behavioural fact recorded against a booker: Cancellation, Noshow, Reschedule, Other. Penalty points are computed from this history, scaled to departure proximity. |
Segment | One 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. |
FareCategory | The class of service or entitlement tier applied to a booking. |
PolicyGroup · Division | Organisational placement of a traveller — division, department, unit, employee level — that policy and approval routing key off. |
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.
| Endpoint | Purpose |
|---|---|
POST /rules/validate | Validate an itinerary against all booking, penalty, and quota rules. |
POST /rules/validator/penalties | Evaluate a booker's penalty points from their history of booking events. |
POST /rules/validator/search | Basic rule validation for a lookup request. |
POST /rules/segment/validate | Check a request against each segment's release time. |
GET /rules/result | Retrieve the outcome of a prior validation. |
PUT /rules/quota/adjust | Set or adjust the seat quota for an employee, scoped to trip reason, passenger type, year, and time frame. |
GET /settings/passengerTypes | The defined passenger types, each with its booking source and ad-hoc flag. |
GET /settings/tripReasons | The defined trip reasons that quotas and entitlements attach to. |
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.
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.
PUT /rules/quota/adjust
{
"employeeId": "E-10293",
"tripReasonId": "training",
"passengerTypeId": "staff",
"year": 2026,
"quotaSeats": 12,
"timeFrame": "PerAnnum"
}
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.
| Endpoint | Purpose |
|---|---|
GET /trips/search | The possible connections for a request across owned, leased, and commercial content. |
GET /trips/locations | All active locations in the network. |
POST /trips/booking/start-session | Open 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/book | Create 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-consistency | Verify that bookings are consistent with segment entries. |
POST /trips/booking/transport/events | Attach a land transport event to a booking — the road leg on the same record. |
POST /trips/booking/{ref}/additionalFields | Upsert client-specific fields on a booking; remove per field key. |
POST /trips/booking/{ref}/external-identifier | Set an external identifier, tying the booking to an upstream system. |
GET /trips/history | The booking history for a booker. |
GET /farecategories/list | All fare categories. |
GET /travelers/profile/{idOrEmail} | The travel profile of an employee — with search, bulk import, and enable/disable endpoints alongside. |
GET /bookers/profiles | All booker profiles — a booker is an independent traveller with an employee id. |
GET /booker/{employeeId}/events | The booking activity of a booker — the event history penalties are computed from. |
POST /asset/utilization | Record 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/import | Move expense-relevant data to and from the finance side. |
POST /elina/update-booking | Synchronise a booking with Elina PMS, so the guesthouse room lives on the same record as the seat. |
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.
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 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"
}
}
]
| Field | Meaning |
|---|---|
claimNumber | The claim the approval belongs to. For business bookings, no payment executes without an approved claim number. |
bookingReference | The booking the claim relates to. |
status · sequenceNumber | The lifecycle state of the claim and this approver's position in the chain — approvals route in sequence under delegation of financial authority. |
approvalDeadline · approvalTimeLimit | When the approval window closes, so time-critical travel is not stranded in a queue. |
requester | The traveller's full organisational placement — division, department, unit, employee level, passenger type, policy group — the coordinates policy and routing key off. |
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.
| Endpoint | Purpose |
|---|---|
POST /securitycenter/manifest | The 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/activities | Per-identity activity — the observability that lets a rogue automation be isolated down to the individual account. |
GET /securitycenter/apps | The applications registered under the security model. |
POST /roles/add · POST /roles/remove | Grant and revoke roles. |
POST /self-register/add · addbulk · DELETE remove | Traveller 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.
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.
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.
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.
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.
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.
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.
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.
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.