Developer Reference
Claim Approvals Webhook Reference
UnityTrip notifies your systems as claims move through their lifecycle: for each subscribed event it sends an HTTP request to your endpoint with a JSON payload. This document describes each event type and its payload.
Delivery basics
- Method:
POST - Content-Type:
application/json - Body: a single JSON object per event.
- Endpoint: the Webhook URL you configure in Ops Manager (the operator console) (Webhooks).
- A webhook fires only for the event types selected in its configuration.
- Always respond with a
2xxstatus code to acknowledge receipt. Any other response, or no response, counts as a failed attempt.
Delivery and security
This reference describes current behaviour and evolves with the platform. When you build a receiver, confirm the specifics with us; we walk every integrator through them.
- Signature: every delivery carries an
X-Webhook-Signatureheader: an HMAC-SHA256 of the raw request body, keyed with the secret you set for that webhook when you create it in Ops Manager, hex-encoded. Recompute it over the bytes you received, before parsing, and compare in constant time; reject the delivery if it does not match. - Retries: up to 10 retries after the first attempt, with exponential backoff starting at one minute (1, 2, 4, 8 minutes and so on, about 17 hours in all). Attempts and their outcomes are visible in Ops Manager.
- Delivery semantics: an event is sent until it is acknowledged, so the same event can arrive again with a new
deliveryId. De-duplicate on the event, not the delivery: its identity is the combination ofclaimId,eventTypeanddata.timestamp. - Source addresses: deliveries do not come from a fixed set of IP addresses. Authenticate by signature rather than by allow-list.
Verification, in outline (Node.js):
const crypto = require("crypto");
function verify(rawBody, header, secret) {
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
return expected.length === header.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(header));
}
Common envelope
Every event shares this top-level structure; data carries the event-specific fields.
| Field | Type | Description |
|---|---|---|
deliveryId | string (UUID) | Unique ID for this individual delivery attempt. A retry carries a new deliveryId for the same event, so de-duplicate on the event, not on this field (see Delivery and security). |
claimId | string (UUID) | Internal unique identifier of the claim. |
claimIdentifier | string | Human-readable claim number (e.g. CLM-01533). |
eventType | string | The event name. Determines the shape of data. |
timestamp | string (ISO 8601) | When the event was generated. |
data | object | Event-specific metadata. See each event below. |
costCenter | object | The cost centre associated with the claim. |
costCenter.id | string (UUID) | Internal cost centre identifier. |
costCenter.name | string | Display name of the cost centre. |
costCenter.identifier | string | Short code for the cost centre (e.g. NEIL1). |
fund | object | The fund associated with the claim. |
fund.id | string (UUID) | Internal fund identifier. |
fund.name | string | Display name of the fund. |
requestTitle | string | Title given to the request. |
tripReason | string | Stated reason for the trip (e.g. Business). |
requestDescription | string | Free-text description of the request. |
link | string (URL) | Deep link to the claim in the UnityTrip web app. |
Common data fields
These fields appear within data across most event types:
| Field | Type | Description |
|---|---|---|
$type | integer | Internal discriminator identifying the event payload type. |
timestamp | string (ISO 8601) | When the underlying action occurred. |
email | string | null | Email of the user associated with the action, when applicable. May be null for system-initiated actions. |
publishedAt | string (ISO 8601) | When the event was published to the delivery queue. |
outOfSyncReason | string | null | Reason the claim was flagged out of sync, if any. Empty string or null when none. |
approvers | array of objects | The approvers assigned to the claim, in approval order. See The approvers array below. |
Event-specific data fields are listed under each event below.
The approvers array
Most events include an approvers array in data: one object per approver in the claim's approval chain, ordered by sequence. It answers who a claim is waiting on without a further API call.
The array is present on ClaimCreatedEvent, ClaimApprovedEvent, ClaimApproverAddedEvent, ClaimUpdatedEvent, ClaimRejectedEvent, ClaimClosedEvent, ClaimRevisionCreated, and ClaimRevisionApprovedEvent. It is not included on ClaimPartialApprovedEvent — see that event for how to advance your pending list.
| Field | Type | Description |
|---|---|---|
approvers[].email | string | Email address of the approver. |
approvers[].name | string | Display name of the approver (e.g. Mr Neil Test). |
approvers[].employeeId | string | The approver's employee identifier in your organisation. |
approvers[].sequence | integer | Position of this approver in the approval chain, starting at 1. Approvers act in ascending sequence order; a claim is fully approved only once the highest sequence has approved. |
approvers[].isSubstitution | boolean | true if this approver is acting as a delegate or substitute for the originally assigned approver. |
Example — a two-approver chain
"approvers": [
{
"email": "first.approver@example.com",
"name": "Mr First Approver",
"employeeId": "11111",
"sequence": 1,
"isSubstitution": false
},
{
"email": "second.approver@example.com",
"name": "Mr Second Approver",
"employeeId": "8888",
"sequence": 2,
"isSubstitution": true
}
]
Tip — pending bookings by approver: To surface what each approver still needs to action, track the open claims fromClaimCreatedEventandClaimRevisionCreatedand group them byapprovers[].email(oremployeeId). The current approver is the one with the lowestsequencewho has not yet approved — show the claim only on their list. When aClaimPartialApprovedEventarrives, advance the claim to the nextsequence. Remove the claim from all pending lists when you receive aClaimApprovedEvent(final approval),ClaimRejectedEvent, orClaimClosedEvent.
ClaimCreatedEvent
Fired when a new claim is created.
Example payload
{
"deliveryId": "8c7b97c2-6cbe-4ceb-9bee-93cb1fcf8bb9",
"claimId": "db9adf7d-1522-45ad-ac61-b789a2156267",
"claimIdentifier": "CLM-01533",
"eventType": "ClaimCreatedEvent",
"timestamp": "2026-06-02T01:12:15.9707421+00:00",
"data": {
"$type": 7,
"timestamp": "2026-06-02T01:12:13.9291571+00:00",
"email": "user@example.com",
"publishedAt": "2026-06-02T01:12:14.5512303+00:00",
"approvers": [
{
"email": "approver@example.com",
"name": "Mr Neil Test",
"employeeId": "11111",
"sequence": 1,
"isSubstitution": false
}
]
},
"costCenter": {
"id": "4beda99c-9efd-4b51-ae32-8fcc1b0b48b2",
"name": "Example Cost Centre",
"identifier": "NEIL1"
},
"fund": {
"id": "50ba2af9-40f7-41b5-8e10-1a8ba774f746",
"name": "Training"
},
"requestTitle": "Example request",
"tripReason": "Business",
"requestDescription": "NEIL1 - Training",
"link": "https://app.unitytrip.app/claims/db9adf7d-1522-45ad-ac61-b789a2156267"
}
data fields
Uses the common envelope and common data fields. $type is 7.
ClaimApprovedEvent
Fired when a claim is fully approved: the last approver in sequence order has approved. Earlier approvals in a chain fire ClaimPartialApprovedEvent.
Example payload
{
"deliveryId": "8ab890dc-95d7-47f8-b69d-573ed1831ffb",
"claimId": "db9adf7d-1522-45ad-ac61-b789a2156267",
"claimIdentifier": "CLM-01533",
"eventType": "ClaimApprovedEvent",
"timestamp": "2026-06-02T01:21:07.3115601+00:00",
"data": {
"$type": 1,
"outOfSyncReason": "",
"timestamp": "2026-06-02T01:21:05.7020226+00:00",
"email": "approver@example.com",
"publishedAt": "2026-06-02T01:21:05.8547919+00:00",
"approvers": [
{
"email": "approver@example.com",
"name": "Mr Neil Test",
"employeeId": "11111",
"sequence": 1,
"isSubstitution": false
}
]
},
"costCenter": {
"id": "4beda99c-9efd-4b51-ae32-8fcc1b0b48b2",
"name": "Example Cost Centre",
"identifier": "NEIL1"
},
"fund": {
"id": "50ba2af9-40f7-41b5-8e10-1a8ba774f746",
"name": "Training"
},
"requestTitle": "Example request",
"tripReason": "Business",
"requestDescription": "NEIL1 - Training",
"link": "https://app.unitytrip.app/claims/db9adf7d-1522-45ad-ac61-b789a2156267"
}
data fields
Uses the common envelope and common data fields. $type is 1.
| Field | Type | Description |
|---|---|---|
outOfSyncReason | string | null | Reason the claim was out of sync, if any, for example when an admin overrides and cancels a claim. |
ClaimPartialApprovedEvent
Fired when one approver in a multi-step approval chain approves but the claim still requires approval from one or more later approvers. The claim now advances to the next sequence. A final ClaimApprovedEvent follows once the last approver acts.
Note: Unlike most events,ClaimPartialApprovedEventdoes not include theapproversarray. Use the chain you captured fromClaimCreatedEvent(orClaimApproverAddedEvent) to determine who the next approver is. The
Example payload
{
"deliveryId": "2e9b40ca-48b2-4c58-89f8-17a47bac21e3",
"claimId": "b2aa03e8-1fcc-437e-9081-7213b7c52563",
"claimIdentifier": "CLM-01536",
"eventType": "ClaimPartialApprovedEvent",
"timestamp": "2026-06-05T00:55:58.2794482+00:00",
"data": {
"$type": 19,
"outOfSyncReason": "",
"timestamp": "2026-06-05T00:55:58.1703789+00:00",
"email": "first.approver@example.com",
"publishedAt": "2026-06-05T00:55:58.1806732+00:00"
},
"costCenter": {
"id": "d6181b11-ff27-487e-bb40-43e1897a6315",
"name": "Example Cost Centre (2 approvers)",
"identifier": "NEIL2"
},
"fund": {
"id": "713a0a07-aac4-4514-9ce0-440e8593448c",
"name": "Business Meetings"
},
"requestTitle": "Example request",
"tripReason": "Business",
"requestDescription": "NEIL2 - Business Meetings",
"link": "https://app.unitytrip.app/claims/b2aa03e8-1fcc-437e-9081-7213b7c52563"
}
data fields
Uses the common envelope and common data fields. $type is 19. The approvers array is not included.
| Field | Type | Description |
|---|---|---|
email | string | Email of the approver who just approved this step. |
outOfSyncReason | string | null | Reason the claim was out of sync, if any, for example when an admin overrides and cancels a claim. |
ClaimUpdatedEvent
Fired when an existing claim is updated.
Example payload
{
"deliveryId": "54d93a54-a99e-411e-9a9c-19849daaf9b2",
"claimId": "75da7441-5a94-43cf-af42-1f9ee060533c",
"claimIdentifier": "CLM-01458",
"eventType": "ClaimUpdatedEvent",
"timestamp": "2026-06-02T01:24:10.4166107+00:00",
"data": {
"$type": 6,
"reason": "Clarification change made2",
"timestamp": "2026-06-02T01:24:10.3362989+00:00",
"email": null,
"publishedAt": "2026-06-02T01:24:10.3746569+00:00",
"approvers": [
{
"email": "approver@example.com",
"name": "Mr Neil Test",
"employeeId": "11111",
"sequence": 1,
"isSubstitution": false
}
]
},
"costCenter": {
"id": "1c143e48-3335-4b88-90d3-721ff20f62c5",
"name": "Example Cost Centre",
"identifier": "NEIL1"
},
"fund": {
"id": "50ba2af9-40f7-41b5-8e10-1a8ba774f746",
"name": "Training"
},
"requestTitle": "Example request",
"tripReason": "Business",
"requestDescription": "NEIL1 - Training",
"link": "https://app.unitytrip.app/claims/75da7441-5a94-43cf-af42-1f9ee060533c"
}
data fields
Uses the common envelope and common data fields. $type is 6.
| Field | Type | Description |
|---|---|---|
reason | string | Reason given for the update. |
ClaimRejectedEvent
Fired when a claim is rejected.
Example payload
{
"deliveryId": "ba4592d2-3c9e-4b60-9bd9-4ffef38159cb",
"claimId": "7d2e71d0-b8eb-4956-9437-3e4be5c646ff",
"claimIdentifier": "CLM-01462",
"eventType": "ClaimRejectedEvent",
"timestamp": "2026-06-02T01:26:32.5960592+00:00",
"data": {
"$type": 2,
"reason": "",
"outOfSyncReason": null,
"timestamp": "2026-06-02T01:26:32.4997626+00:00",
"email": null,
"publishedAt": "2026-06-02T01:26:32.5627479+00:00",
"approvers": [
{
"email": "approver@example.com",
"name": "Mr Neil Test",
"employeeId": "11111",
"sequence": 1,
"isSubstitution": false
}
]
},
"costCenter": {
"id": "1c143e48-3335-4b88-90d3-721ff20f62c5",
"name": "Example Cost Centre",
"identifier": "NEIL1"
},
"fund": {
"id": "50ba2af9-40f7-41b5-8e10-1a8ba774f746",
"name": "Training"
},
"requestTitle": "Example request",
"tripReason": "Business",
"requestDescription": "NEIL1 - Training",
"link": "https://app.unitytrip.app/claims/7d2e71d0-b8eb-4956-9437-3e4be5c646ff"
}
data fields
Uses the common envelope and common data fields. $type is 2.
| Field | Type | Description |
|---|---|---|
reason | string | Reason given for the rejection. May be an empty string. |
outOfSyncReason | string | null | Reason the claim was out of sync, if any, for example when an admin overrides and cancels a claim. |
ClaimClosedEvent
Fired when a claim is closed.
Example payload
{
"deliveryId": "ad84ae04-c2fb-49fb-901c-7f8fecacfada",
"claimId": "b8fd7e64-5789-42bc-ae5c-087ee3812eb3",
"claimIdentifier": "CLM-01532",
"eventType": "ClaimClosedEvent",
"timestamp": "2026-06-02T01:09:40.879154+00:00",
"data": {
"$type": 8,
"reason": "Closed by admin",
"byAdmin": true,
"timestamp": "2026-06-02T01:09:39.777797+00:00",
"email": null,
"publishedAt": "2026-06-02T01:09:39.8600662+00:00",
"approvers": [
{
"email": "approver@example.com",
"name": "Mr Neil Test",
"employeeId": "11111",
"sequence": 1,
"isSubstitution": false
}
]
},
"costCenter": {
"id": "4beda99c-9efd-4b51-ae32-8fcc1b0b48b2",
"name": "Example Cost Centre",
"identifier": "NEIL1"
},
"fund": {
"id": "50ba2af9-40f7-41b5-8e10-1a8ba774f746",
"name": "Training"
},
"requestTitle": "Example request",
"tripReason": "Business",
"requestDescription": "NEIL1 - Training",
"link": "https://app.unitytrip.app/claims/b8fd7e64-5789-42bc-ae5c-087ee3812eb3"
}
data fields
Uses the common envelope and common data fields. $type is 8.
| Field | Type | Description |
|---|---|---|
reason | string | Reason the claim was closed. |
byAdmin | boolean | true if the claim was closed by an administrator. |
ClaimRevisionCreated
Fired when a revision of a claim is created.
Example payload
{
"deliveryId": "182967b4-f015-4f0b-b1b9-67fb3c15cef0",
"claimId": "db9adf7d-1522-45ad-ac61-b789a2156267",
"claimIdentifier": "CLM-01533",
"eventType": "ClaimRevisionCreated",
"timestamp": "2026-06-02T01:26:05.8002028+00:00",
"data": {
"$type": 10,
"reason": "2 days testing of change",
"version": 1,
"outOfSyncReason": null,
"timestamp": "2026-06-02T01:26:05.7511428+00:00",
"email": null,
"publishedAt": "2026-06-02T01:26:05.778295+00:00",
"approvers": [
{
"email": "approver@example.com",
"name": "Mr Neil Test",
"employeeId": "11111",
"sequence": 1,
"isSubstitution": false
}
]
},
"costCenter": {
"id": "4beda99c-9efd-4b51-ae32-8fcc1b0b48b2",
"name": "Example Cost Centre",
"identifier": "NEIL1"
},
"fund": {
"id": "50ba2af9-40f7-41b5-8e10-1a8ba774f746",
"name": "Training"
},
"requestTitle": "Example request",
"tripReason": "Business",
"requestDescription": "NEIL1 - Training",
"link": "https://app.unitytrip.app/claims/db9adf7d-1522-45ad-ac61-b789a2156267"
}
data fields
Uses the common envelope and common data fields. $type is 10.
| Field | Type | Description |
|---|---|---|
reason | string | Reason given for the revision. |
version | integer | Version number of the revision. |
outOfSyncReason | string | null | Reason the claim was out of sync, if any, for example when an admin overrides and cancels a claim. |
ClaimRevisionApprovedEvent
Fired when a claim revision is approved.
Example payload
{
"deliveryId": "c2fd953c-0038-496a-a0ad-4beb36df4a5a",
"claimId": "db9adf7d-1522-45ad-ac61-b789a2156267",
"claimIdentifier": "CLM-01533",
"eventType": "ClaimRevisionApprovedEvent",
"timestamp": "2026-06-02T01:28:24.2506214+00:00",
"data": {
"$type": 14,
"version": 1,
"outOfSyncReason": "",
"timestamp": "2026-06-02T01:28:24.1943104+00:00",
"email": "approver@example.com",
"publishedAt": "2026-06-02T01:28:24.2195565+00:00",
"approvers": [
{
"email": "approver@example.com",
"name": "Mr Neil Test",
"employeeId": "11111",
"sequence": 1,
"isSubstitution": false
}
]
},
"costCenter": {
"id": "4beda99c-9efd-4b51-ae32-8fcc1b0b48b2",
"name": "Example Cost Centre",
"identifier": "NEIL1"
},
"fund": {
"id": "50ba2af9-40f7-41b5-8e10-1a8ba774f746",
"name": "Training"
},
"requestTitle": "Example request",
"tripReason": "Business",
"requestDescription": "NEIL1 - Training",
"link": "https://app.unitytrip.app/claims/db9adf7d-1522-45ad-ac61-b789a2156267"
}
data fields
Uses the common envelope and common data fields. $type is 14.
| Field | Type | Description |
|---|---|---|
version | integer | Version number of the approved revision. |
outOfSyncReason | string | null | Reason the claim was out of sync, if any, for example when an admin overrides and cancels a claim. |
ClaimApproverAddedEvent
Fired when an approver is added to a claim's approval chain — for example when a substitute or an additional approver is assigned. In addition to the common approvers array, this event's data carries an approver object with the full profile of the person who was added.
Example payload
{
"deliveryId": "aeacf3d3-e333-4962-96b8-2be2f4f2ee55",
"claimId": "26c8d4c2-ad48-4ab1-b44f-f556294fcb2b",
"claimIdentifier": "CLM-01535",
"eventType": "ClaimApproverAddedEvent",
"timestamp": "2026-06-05T00:41:33.2669466+00:00",
"data": {
"$type": 18,
"approver": {
"title": "Mr",
"firstname": "Second",
"lastname": "Approver",
"displayname": "Mr Second Approver",
"normalizedSearchValue": "mr second approver second.approver@example.com 45454",
"email": "second.approver@example.com",
"phone": "45454",
"employeeId": "8888",
"employeeLevel": "Grade 3",
"division": "Operations",
"department": "Field Services",
"unit": "Rotations",
"passengerType": "Staff",
"subPassengerType": 0,
"locationId": "100",
"policyGroupId": "1",
"id": "6e761b17-9dce-4494-9373-316c779a3469"
},
"byAdmin": false,
"timestamp": "2026-06-05T00:41:33.2103192+00:00",
"email": "second.approver@example.com",
"publishedAt": "2026-06-05T00:41:33.2191833+00:00",
"approvers": [
{
"email": "first.approver@example.com",
"name": "Mr First Approver",
"employeeId": "11111",
"sequence": 1,
"isSubstitution": false
},
{
"email": "second.approver@example.com",
"name": "Mr Second Approver",
"employeeId": "8888",
"sequence": 2,
"isSubstitution": true
}
]
},
"costCenter": {
"id": "d6181b11-ff27-487e-bb40-43e1897a6315",
"name": "Example Cost Centre (2 approvers)",
"identifier": "NEIL2"
},
"fund": {
"id": "713a0a07-aac4-4514-9ce0-440e8593448c",
"name": "Business Meetings"
},
"requestTitle": "Example request",
"tripReason": "Business",
"requestDescription": "NEIL2 - Business Meetings",
"link": "https://app.unitytrip.app/claims/26c8d4c2-ad48-4ab1-b44f-f556294fcb2b"
}
data fields
Uses the common envelope and common data fields. $type is 18.
| Field | Type | Description |
|---|---|---|
byAdmin | boolean | true if an administrator added the approver. |
approver | object | Full profile of the approver who was added. See below. |
The approver object
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Internal unique identifier of the approver. |
title | string | Honorific title (e.g. Mr). |
firstname | string | Given name. |
lastname | string | Family name. |
displayname | string | Full display name. |
normalizedSearchValue | string | Lower-cased, concatenated search string used internally for lookup. Not intended for display. |
email | string | Email address. |
phone | string | Contact phone number. |
employeeId | string | Employee identifier in your organisation. |
employeeLevel | string | Employee grade or band (e.g. Grade 3). |
division | string | Division the approver belongs to. |
department | string | Department the approver belongs to. |
unit | string | Organisational unit. |
passengerType | string | Passenger classification (e.g. Staff). |
subPassengerType | integer | Numeric sub-classification of the passenger type. |
locationId | string | Identifier of the approver's location. |
policyGroupId | string | Identifier of the policy group the approver is assigned to. |
Quick reference: event types and $type values
| Event type | $type | Description |
|---|---|---|
ClaimCreatedEvent | 7 | A new claim is created. |
ClaimApprovedEvent | 1 | A claim is fully approved (final approval). |
ClaimPartialApprovedEvent | 19 | One approver in a multi-step chain approves; further approval is still required. |
ClaimApproverAddedEvent | 18 | An approver is added to the claim's approval chain. |
ClaimUpdatedEvent | 6 | An existing claim is updated. |
ClaimRejectedEvent | 2 | A claim is rejected. |
ClaimClosedEvent | 8 | A claim is closed. |
ClaimRevisionCreated | 10 | A revision of a claim is created. |
ClaimRevisionApprovedEvent | 14 | A claim revision is approved. |
Note: Always switch on theeventTypestring rather than the numeric$typediscriminator, as$typeis an internal value subject to change.
Example payloads above use placeholder emails, names, titles, URLs, identifiers and organisational values. Real payloads will contain your organisation's actual data.
The approvals endpoint these events complement is in the API reference.