Skip to main content

Disruption and mass change

Record a disruption, plan a mass change (line update or re-accommodation with price protection), commit it with its commit token, retry failures and roll back.

OperationEndpoint
List disruptionsGET /oms-ops/disruptions
Record a disruptionPOST /oms-ops/disruptions
Read a disruptionGET /oms-ops/disruptions/{disruptionId}
Read the impact of a disruptionGET /oms-ops/disruptions/{disruptionId}/impact
List a disruption's mass changesGET /oms-ops/disruptions/{disruptionId}/mass-changes
Plan a mass changePOST /oms-ops/disruptions/{disruptionId}/mass-changes
Resolve, supersede or reopen a disruptionPOST /oms-ops/disruptions/{disruptionId}/status
List mass changesGET /oms-ops/mass-changes
Read a mass changeGET /oms-ops/mass-changes/{changeId}
Commit a planned mass changePOST /oms-ops/mass-changes/{changeId}/commit
Dry-run the pending itemsPOST /oms-ops/mass-changes/{changeId}/dry-run
List a mass change's itemsGET /oms-ops/mass-changes/{changeId}/items
Record a guest's choice of the alternative or a refundPOST /oms-ops/mass-changes/{changeId}/items/{tripId}/guest-choice
Retry failed itemsPOST /oms-ops/mass-changes/{changeId}/retry
Roll back a mass changePOST /oms-ops/mass-changes/{changeId}/rollback

List disruptions​

GET/oms-ops/disruptions

Parameters

NameInRequiredNotes
statusqueryopen
limitqueryDefault 50, maximum 200.; integer
cursorqueryThe nextCursor of the previous page.

Response 200

Errors
StatusCodeMeaning
400invalid_requestThe request failed validation. The message names the first failing field; oms-ops and group surfaces also return every issue in details.
400invalid_cursorThe pagination cursor is malformed.
401unauthenticatedThe bearer token is missing, invalid, expired or minted for another purpose.
403scope_forbiddenThe token is scoped to an agency or an access scope; this surface requires an unscoped tenant token.
404order_operations_not_enabledThe order operations policy section is not configured for the tenant.
404oms_ops_not_enabledOrder operations are not enabled for the tenant.
500internalUnexpected failure. Retry with the same Idempotency-Key.

Record a disruption​

POST/oms-ops/disruptions

Headers: Idempotency-Key, plus X-Request-Id.

Request body

FieldTypeRequiredNotes
actorobjectyes
actor.channelstringmax 60 chars
actor.idstringyes1–200 chars
actor.namestringmax 200 chars
actor.rolestringyes1–100 chars
descriptionstringyes1–2000 chars
disruptionIdstring1–200 chars
kindenum: itinerary_change, service_unavailable, departure_cancelled, transport_change, otheryes
occurredAtstring (date-time)
reasonCodestringyes1–60 chars
scopeobjectyes
scope.departureRefsarray of string
scope.lineStatusesarray of string
scope.productKindsarray of string
scope.productRefsarray of string
scope.tripIdsarray of string
sourceSystemstringmax 100 chars

Schema: apps/journeys/src/oms-ops/contracts.ts (RecordDisruptionSchema)

Response 201

POST/oms-ops/disruptionsRecord a cancelled departure
Request
curl -X POST "$BASE/oms-ops/disruptions" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Request-Id: req-0041" \
-H "Content-Type: application/json" \
-d '{
"kind": "departure_cancelled",
"reasonCode": "ship_technical",
"description": "Departure of 12 April 2027 cancelled; guests are offered the 19 April departure.",
"scope": {
"departureRefs": [
"MED7-2027-04-12"
],
"productKinds": [
"cruise"
]
},
"sourceSystem": "reservation",
"actor": {
"id": "ops-0007",
"name": "Paolo Ricci",
"role": "ops_manager",
"channel": "back_office"
}
}'
Response
HTTP/1.1 201
X-Request-Id: req-0041
{
"disruptionId": "dis_6aa95d68-a42d-4d47-8170-95390ba977c3",
"tenantId": "meridian",
"kind": "departure_cancelled",
"origin": "disruption",
"reasonCode": "ship_technical",
"description": "Departure of 12 April 2027 cancelled; guests are offered the 19 April departure.",
"scope": {
"departureRefs": [
"MED7-2027-04-12"
],
"productRefs": [],
"productKinds": [
"cruise"
],
"tripIds": [],
"lineStatuses": []
},
"occurredAt": "2026-11-02T09:30:17.000Z",
"sourceSystem": "reservation",
"status": "open",
"actor": {
"id": "ops-0007",
"name": "Paolo Ricci",
"role": "ops_manager",
"channel": "back_office"
},
"createdAt": "2026-11-02T09:30:17.000Z",
"updatedAt": "2026-11-02T09:30:17.000Z"
}
Errors
StatusCodeMeaning
400invalid_requestThe request failed validation. The message names the first failing field; oms-ops and group surfaces also return every issue in details.
401unauthenticatedThe bearer token is missing, invalid, expired or minted for another purpose.
403scope_forbiddenThe token is scoped to an agency or an access scope; this surface requires an unscoped tenant token.
404oms_ops_not_enabledOrder operations are not enabled for the tenant.
409idempotency_in_progressA request with this Idempotency-Key is still running. Retry after the Retry-After interval.
422idempotency_key_reusedThe Idempotency-Key was used for a different request.
500internalUnexpected failure. Retry with the same Idempotency-Key.

Read a disruption​

GET/oms-ops/disruptions/{disruptionId}

Parameters

NameInRequiredNotes
disruptionIdpathyes

Response 200

Errors
StatusCodeMeaning
400invalid_requestThe request failed validation. The message names the first failing field; oms-ops and group surfaces also return every issue in details.
401unauthenticatedThe bearer token is missing, invalid, expired or minted for another purpose.
403scope_forbiddenThe token is scoped to an agency or an access scope; this surface requires an unscoped tenant token.
404disruption_not_foundThe disruption does not exist.
404oms_ops_not_enabledOrder operations are not enabled for the tenant.
500internalUnexpected failure. Retry with the same Idempotency-Key.

Read the impact of a disruption​

GET/oms-ops/disruptions/{disruptionId}/impact

Parameters

NameInRequiredNotes
disruptionIdpathyes

Response 200

Errors
StatusCodeMeaning
400invalid_requestThe request failed validation. The message names the first failing field; oms-ops and group surfaces also return every issue in details.
401unauthenticatedThe bearer token is missing, invalid, expired or minted for another purpose.
403scope_forbiddenThe token is scoped to an agency or an access scope; this surface requires an unscoped tenant token.
404order_operations_not_enabledThe order operations policy section is not configured for the tenant.
404oms_ops_not_enabledOrder operations are not enabled for the tenant.
500internalUnexpected failure. Retry with the same Idempotency-Key.

List a disruption's mass changes​

GET/oms-ops/disruptions/{disruptionId}/mass-changes

Parameters

NameInRequiredNotes
disruptionIdpathyes

Response 200

Errors
StatusCodeMeaning
400invalid_requestThe request failed validation. The message names the first failing field; oms-ops and group surfaces also return every issue in details.
401unauthenticatedThe bearer token is missing, invalid, expired or minted for another purpose.
403scope_forbiddenThe token is scoped to an agency or an access scope; this surface requires an unscoped tenant token.
404oms_ops_not_enabledOrder operations are not enabled for the tenant.
500internalUnexpected failure. Retry with the same Idempotency-Key.

Plan a mass change​

POST/oms-ops/disruptions/{disruptionId}/mass-changes

Returns the planned change with a preview of every affected trip and a commitToken. Nothing is applied until the change is committed with that token.

Parameters

NameInRequiredNotes
disruptionIdpathyes

Headers: Idempotency-Key, plus X-Request-Id.

Request body

FieldTypeRequiredNotes
actorobjectyes
actor.channelstringmax 60 chars
actor.idstringyes1–200 chars
actor.namestringmax 200 chars
actor.rolestringyes1–100 chars
changeIdstring1–200 chars
reasonstringmax 2000 chars
specone of several shapesyes

Schema: apps/journeys/src/oms-ops/contracts.ts (PlanMassChangeSchema)

Response 201

POST/oms-ops/disruptions/dis_6aa95d68-a42d-4d47-8170-95390ba977c3/mass-changesPlan the re-accommodation with same-price protection
Request
curl -X POST "$BASE/oms-ops/disruptions/dis_6aa95d68-a42d-4d47-8170-95390ba977c3/mass-changes" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Request-Id: req-0042" \
-H "Content-Type: application/json" \
-d '{
"spec": {
"kind": "re_accommodation",
"targetDepartureRef": "MED7-2027-04-19",
"categoryMapping": {},
"protection": {
"ruleId": "same-price"
},
"targetPrices": {
"BALCONY-B2": {
"amountMinor": 252000
}
}
},
"actor": {
"id": "ops-0007",
"name": "Paolo Ricci",
"role": "ops_manager",
"channel": "back_office"
},
"reason": "Move affected guests to the next departure"
}'
Response
HTTP/1.1 201
X-Request-Id: req-0042
{
"change": {
"changeId": "mc_510291cb-9863-4349-b300-eaaf0d44f0d6",
"tenantId": "meridian",
"disruptionId": "dis_6aa95d68-a42d-4d47-8170-95390ba977c3",
"kind": "re_accommodation",
"status": "planned",
"spec": {
"kind": "re_accommodation",
"targetDepartureRef": "MED7-2027-04-19",
"categoryMapping": {},
"protection": {
"ruleId": "same-price"
},
"targetPrices": {
"BALCONY-B2": {
"amountMinor": 252000,
"perTraveller": false
}
}
},
"rollbackOf": null,
"rolledBackBy": null,
"commitToken": "3f1d37d6df1a7b076339f1f93da107f4",
"population": {
"trips": 3,
"lines": 3
},
"counts": {
"total": 3,
"pending": 3,
"applied": 0,
"failed": 0,
"needs_person": 0,
"skipped": 0,
"rolled_back": 0,
"rollback_conflict": 0
},
"actor": {
"id": "ops-0007",
"name": "Paolo Ricci",
"role": "ops_manager",
"channel": "back_office"
},
"reason": "Move affected guests to the next departure",
"createdAt": "2026-11-02T09:30:54.000Z",
"updatedAt": "2026-11-02T09:30:54.000Z",
"startedAt": null,
"finishedAt": null,
"throughputPerSecond": null
},
"preview": [
{
"changeId": "mc_510291cb-9863-4349-b300-eaaf0d44f0d6",
"tripId": "TRP-F0A893B4CB27",
"status": "pending",
"attempts": 0,
"error": null,
"plan": {
"lines": [
{
"lineId": "L-4364586D534F",
"expectedVersion": 3
}
]
},
"before": null,
"after": null,
"outcome": null,
"updatedAt": "2026-11-02T09:30:54.000Z"
},
{
"changeId": "mc_510291cb-9863-4349-b300-eaaf0d44f0d6",
"tripId": "TRP-976512C541CF",
"status": "pending",
"attempts": 0,
"error": null,
"plan": {
"lines": [
{
"lineId": "L-A349D3520C1B",
"expectedVersion": 3
}
]
},
"before": null,
"after": null,
"outcome": null,
"updatedAt": "2026-11-02T09:30:54.000Z"
},
{
"changeId": "mc_510291cb-9863-4349-b300-eaaf0d44f0d6",
"tripId": "TRP-8232B7FF83C4",
"status": "pending",
"attempts": 0,
"error": null,
"plan": {
"lines": [
{
"lineId": "L-0A54E44D0375",
"expectedVersion": 4
}
]
},
"before": null,
"after": null,
"outcome": null,
"updatedAt": "2026-11-02T09:30:54.000Z"
}
]
}
Errors
StatusCodeMeaning
400invalid_requestThe request failed validation. The message names the first failing field; oms-ops and group surfaces also return every issue in details.
401unauthenticatedThe bearer token is missing, invalid, expired or minted for another purpose.
403scope_forbiddenThe token is scoped to an agency or an access scope; this surface requires an unscoped tenant token.
404oms_ops_not_enabledOrder operations are not enabled for the tenant.
409idempotency_in_progressA request with this Idempotency-Key is still running. Retry after the Retry-After interval.
422idempotency_key_reusedThe Idempotency-Key was used for a different request.
500internalUnexpected failure. Retry with the same Idempotency-Key.

Resolve, supersede or reopen a disruption​

POST/oms-ops/disruptions/{disruptionId}/status

Parameters

NameInRequiredNotes
disruptionIdpathyes

Headers: Idempotency-Key, plus X-Request-Id.

Request body

FieldTypeRequiredNotes
actorobjectyes
actor.channelstringmax 60 chars
actor.idstringyes1–200 chars
actor.namestringmax 200 chars
actor.rolestringyes1–100 chars
reasonstringyes1–2000 chars
statusenum: open, resolved, supersededyes
supersededBystring1–200 chars

Schema: apps/journeys/src/oms-ops/contracts.ts (DisruptionStatusChangeSchema)

Response 200

Errors
StatusCodeMeaning
400invalid_requestThe request failed validation. The message names the first failing field; oms-ops and group surfaces also return every issue in details.
401unauthenticatedThe bearer token is missing, invalid, expired or minted for another purpose.
403scope_forbiddenThe token is scoped to an agency or an access scope; this surface requires an unscoped tenant token.
404oms_ops_not_enabledOrder operations are not enabled for the tenant.
409mass_change_runningA mass change for this disruption is still running.
409idempotency_in_progressA request with this Idempotency-Key is still running. Retry after the Retry-After interval.
422idempotency_key_reusedThe Idempotency-Key was used for a different request.
500internalUnexpected failure. Retry with the same Idempotency-Key.

List mass changes​

GET/oms-ops/mass-changes

Parameters

NameInRequiredNotes
statusquery
disruptionIdquery
limitqueryDefault 50, maximum 200.; integer
offsetqueryinteger

Response 200

Errors
StatusCodeMeaning
400invalid_requestThe request failed validation. The message names the first failing field; oms-ops and group surfaces also return every issue in details.
401unauthenticatedThe bearer token is missing, invalid, expired or minted for another purpose.
403scope_forbiddenThe token is scoped to an agency or an access scope; this surface requires an unscoped tenant token.
404order_operations_not_enabledThe order operations policy section is not configured for the tenant.
404oms_ops_not_enabledOrder operations are not enabled for the tenant.
500internalUnexpected failure. Retry with the same Idempotency-Key.

Read a mass change​

GET/oms-ops/mass-changes/{changeId}

Parameters

NameInRequiredNotes
changeIdpathyes

Response 200

GET/oms-ops/mass-changes/mc_510291cb-9863-4349-b300-eaaf0d44f0d6Read the mass change
Request
curl -X GET "$BASE/oms-ops/mass-changes/mc_510291cb-9863-4349-b300-eaaf0d44f0d6" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Request-Id: req-0044"
Response
HTTP/1.1 200
X-Request-Id: req-0044
{
"kind": "re_accommodation",
"spec": {
"kind": "re_accommodation",
"protection": {
"ruleId": "same-price"
},
"targetPrices": {
"BALCONY-B2": {
"amountMinor": 252000,
"perTraveller": false
}
},
"categoryMapping": {},
"targetDepartureRef": "MED7-2027-04-19"
},
"actor": {
"id": "ops-0007",
"name": "Paolo Ricci",
"role": "ops_manager",
"channel": "back_office"
},
"counts": {
"total": 3,
"pending": 0,
"applied": 3,
"failed": 0,
"needs_person": 0,
"skipped": 0,
"rolled_back": 0,
"rollback_conflict": 0
},
"reason": "Move affected guests to the next departure",
"status": "completed",
"changeId": "mc_510291cb-9863-4349-b300-eaaf0d44f0d6",
"tenantId": "meridian",
"createdAt": "2026-11-02T09:30:54.000Z",
"startedAt": "2026-11-02T09:31:31.000Z",
"updatedAt": "2026-11-02T09:30:54.000Z",
"finishedAt": "2026-11-02T09:31:31.000Z",
"population": {
"lines": 3,
"trips": 3
},
"rollbackOf": null,
"commitToken": "3f1d37d6df1a7b076339f1f93da107f4",
"disruptionId": "dis_6aa95d68-a42d-4d47-8170-95390ba977c3",
"rolledBackBy": null,
"throughputPerSecond": 3000,
"contact": {
"requested": 8,
"trips": 3
}
}
Errors
StatusCodeMeaning
400invalid_requestThe request failed validation. The message names the first failing field; oms-ops and group surfaces also return every issue in details.
401unauthenticatedThe bearer token is missing, invalid, expired or minted for another purpose.
403scope_forbiddenThe token is scoped to an agency or an access scope; this surface requires an unscoped tenant token.
404oms_ops_not_enabledOrder operations are not enabled for the tenant.
500internalUnexpected failure. Retry with the same Idempotency-Key.

Commit a planned mass change​

POST/oms-ops/mass-changes/{changeId}/commit

Parameters

NameInRequiredNotes
changeIdpathyes

Headers: Idempotency-Key, plus X-Request-Id.

Request body

FieldTypeRequiredNotes
commitTokenstringyes
actorobjectyes
actor.channelstringmax 60 chars
actor.idstringyes1–200 chars
actor.namestringmax 200 chars
actor.rolestringyes1–100 chars

Response 200

POST/oms-ops/mass-changes/mc_510291cb-9863-4349-b300-eaaf0d44f0d6/commitCommit the planned change with its commit token
Request
curl -X POST "$BASE/oms-ops/mass-changes/mc_510291cb-9863-4349-b300-eaaf0d44f0d6/commit" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Request-Id: req-0043" \
-H "Content-Type: application/json" \
-d '{
"commitToken": "3f1d37d6df1a7b076339f1f93da107f4",
"actor": {
"id": "ops-0007",
"name": "Paolo Ricci",
"role": "ops_manager",
"channel": "back_office"
}
}'
Response
HTTP/1.1 200
X-Request-Id: req-0043
{
"kind": "re_accommodation",
"spec": {
"kind": "re_accommodation",
"protection": {
"ruleId": "same-price"
},
"targetPrices": {
"BALCONY-B2": {
"amountMinor": 252000,
"perTraveller": false
}
},
"categoryMapping": {},
"targetDepartureRef": "MED7-2027-04-19"
},
"actor": {
"id": "ops-0007",
"name": "Paolo Ricci",
"role": "ops_manager",
"channel": "back_office"
},
"counts": {
"total": 3,
"pending": 0,
"applied": 3,
"failed": 0,
"needs_person": 0,
"skipped": 0,
"rolled_back": 0,
"rollback_conflict": 0
},
"reason": "Move affected guests to the next departure",
"status": "completed",
"changeId": "mc_510291cb-9863-4349-b300-eaaf0d44f0d6",
"tenantId": "meridian",
"createdAt": "2026-11-02T09:30:54.000Z",
"startedAt": "2026-11-02T09:31:31.000Z",
"updatedAt": "2026-11-02T09:30:54.000Z",
"finishedAt": "2026-11-02T09:31:31.000Z",
"population": {
"lines": 3,
"trips": 3
},
"rollbackOf": null,
"commitToken": "3f1d37d6df1a7b076339f1f93da107f4",
"disruptionId": "dis_6aa95d68-a42d-4d47-8170-95390ba977c3",
"rolledBackBy": null,
"throughputPerSecond": 3000,
"contact": {
"requested": 8,
"trips": 3
}
}
Errors
StatusCodeMeaning
400invalid_requestThe request failed validation. The message names the first failing field; oms-ops and group surfaces also return every issue in details.
401unauthenticatedThe bearer token is missing, invalid, expired or minted for another purpose.
403scope_forbiddenThe token is scoped to an agency or an access scope; this surface requires an unscoped tenant token.
404oms_ops_not_enabledOrder operations are not enabled for the tenant.
409commit_token_mismatchThe commit token does not match the planned change.
409mass_change_not_plannedThe mass change is not in the planned state.
409idempotency_in_progressA request with this Idempotency-Key is still running. Retry after the Retry-After interval.
422idempotency_key_reusedThe Idempotency-Key was used for a different request.
500internalUnexpected failure. Retry with the same Idempotency-Key.

Dry-run the pending items​

POST/oms-ops/mass-changes/{changeId}/dry-run

Parameters

NameInRequiredNotes
changeIdpathyes

Headers: Idempotency-Key, plus X-Request-Id.

Response 200

Errors
StatusCodeMeaning
400invalid_requestThe request failed validation. The message names the first failing field; oms-ops and group surfaces also return every issue in details.
401unauthenticatedThe bearer token is missing, invalid, expired or minted for another purpose.
403scope_forbiddenThe token is scoped to an agency or an access scope; this surface requires an unscoped tenant token.
404order_operations_not_enabledThe order operations policy section is not configured for the tenant.
404oms_ops_not_enabledOrder operations are not enabled for the tenant.
409idempotency_in_progressA request with this Idempotency-Key is still running. Retry after the Retry-After interval.
422idempotency_key_reusedThe Idempotency-Key was used for a different request.
500internalUnexpected failure. Retry with the same Idempotency-Key.

List a mass change's items​

GET/oms-ops/mass-changes/{changeId}/items

Parameters

NameInRequiredNotes
changeIdpathyes
statusquery
outcomequery
marketquery
channelquery
limitqueryDefault 200, maximum 1000.; integer
offsetqueryinteger

Response 200

GET/oms-ops/mass-changes/mc_510291cb-9863-4349-b300-eaaf0d44f0d6/itemsRead the per-trip items
Request
curl -X GET "$BASE/oms-ops/mass-changes/mc_510291cb-9863-4349-b300-eaaf0d44f0d6/items" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Request-Id: req-0045"
Response
HTTP/1.1 200
X-Request-Id: req-0045
{
"items": [
{
"changeId": "mc_510291cb-9863-4349-b300-eaaf0d44f0d6",
"tripId": "TRP-F0A893B4CB27",
"status": "applied",
"attempts": 1,
"error": null,
"plan": {
"lines": [
{
"lineId": "L-4364586D534F",
"expectedVersion": 3
}
]
},
"before": [
{
"poId": "PO-16FF30EC289C",
"roles": {
"payer": {
"ref": "CUST-20419",
"name": "Giulia Rossi"
},
"booker": {
"ref": "CUST-20419",
"name": "Giulia Rossi"
},
"travellers": [
{
"ref": "PAX-01"
},
{
"ref": "PAX-02"
}
]
},
"lineId": "L-4364586D534F",
"market": "IT",
"status": "booked",
"tripId": "TRP-F0A893B4CB27",
"channel": "web",
"groupId": null,
"currency": "EUR",
"updatedAt": "2026-11-02T09:06:42.000Z",
"attributes": {
"poType": "standard",
"source": "order_orchestration",
"offerId": "ALL-INCLUSIVE-2027",
"quantity": 1,
"poSequence": 1,
"departureAt": "2027-04-12T17:00:00Z",
"externalRef": "RSV-66676AEB-000001",
"orderVersion": 4,
"promotionIds": [
"EARLY-BOOKING-10"
],
"quoteLineRef": "q-line-1",
"sourceSystem": "reservation",
"billingStatus": "not_billed",
"paymentStatus": "not_due",
"reservationRef": "RSV-66676AEB-000001",
"tripCustomerRef": "CUST-20419",
"fulfilmentStatus": "confirmed"
},
"productRef": "MED7-2027-04-12",
"amountMinor": 238000,
"categoryRef": "BALCONY-B2",
"lineVersion": 3,
"productKind": "cruise",
"departureRef": "MED7-2027-04-12"
}
],
"after": [
{
"poId": "PO-16FF30EC289C",
"roles": {
"payer": {
"ref": "CUST-20419",
"name": "Giulia Rossi"
},
"booker": {
"ref": "CUST-20419",
"name": "Giulia Rossi"
},
"travellers": [
{
"ref": "PAX-01"
},
{
"ref": "PAX-02"
}
]
},
"lineId": "L-4364586D534F",
"market": "IT",
"status": "booked",
"tripId": "TRP-F0A893B4CB27",
"channel": "web",
"groupId": null,
"currency": "EUR",
"updatedAt": "2026-11-02T09:31:31.000Z",
"attributes": {
"poType": "standard",
"source": "order_orchestration",
"offerId": "ALL-INCLUSIVE-2027",
"quantity": 1,
"poSequence": 1,
"departureAt": "2027-04-12T17:00:00Z",
"externalRef": "RSV-66676AEB-000001",
"orderVersion": 4,
"promotionIds": [
"EARLY-BOOKING-10"
],
"quoteLineRef": "q-line-1",
"sourceSystem": "reservation",
"billingStatus": "not_billed",
"paymentStatus": "not_due",
"reservationRef": "RSV-66676AEB-000001",
"reAccommodation": {
"upgradedTo": null,
"disruptionId": "dis_6aa95d68-a42d-4d47-8170-95390ba977c3",
"fromProductRef": "MED7-2027-04-12",
"protectionRule": "same_price",
"fromCategoryRef": "BALCONY-B2",
"fromDepartureRef": "MED7-2027-04-12",
"protectionRuleId": "same-price",
"originalAmountMinor": 238000
},
"tripCustomerRef": "CUST-20419",
"fulfilmentStatus": "confirmed"
},
"productRef": "MED7-2027-04-12",
"amountMinor": 238000,
"categoryRef": "BALCONY-B2",
"lineVersion": 4,
"productKind": "cruise",
"departureRef": "MED7-2027-04-19"
}
],
"outcome": {
"rule": "same_price",
"lines": {
"L-4364586D534F": {
"amountMinor": 238000,
"explanation": [
{
"flag": "individual",
"mode": "select",
"basis": "booking",
"input": 0,
"layer": "target_price",
"reason": {
"key": "price_protection.target_price",
"params": {
"category": "BALCONY-B2",
"departure": "MED7-2027-04-19",
"travellers": 2
}
},
"ruleId": null,
"source": {
"kind": "runtime",
"condition": "target:MED7-2027-04-19"
},
"contribution": 252000,
"runningTotal": 252000
},
{
"flag": "individual",
"mode": "replace",
"basis": "booking",
"input": 252000,
"layer": "price_protection",
"reason": {
"key": "price_protection.same_price",
"params": {
"target": 252000,
"original": 238000
}
},
"ruleId": "same-price",
"source": {
"kind": "runtime",
"condition": "disruption:dis_6aa95d68-a42d-4d47-8170-95390ba977c3"
},
"contribution": -14000,
"runningTotal": 238000
}
],
"targetMinor": 252000,
"originalMinor": 238000
}
},
"ruleId": "same-price",
"priceDeltaMinor": 0
},
"updatedAt": "2026-11-02T09:31:31.000Z"
},
{
"changeId": "mc_510291cb-9863-4349-b300-eaaf0d44f0d6",
"tripId": "TRP-976512C541CF",
"status": "applied",
"attempts": 1,
"error": null,
"plan": {
"lines": [
{
"lineId": "L-A349D3520C1B",
"expectedVersion": 3
}
]
},
"before": [
{
"poId": "PO-B3A0355DEB58",
"roles": {
"travellers": [
{
"ref": "PAX-01"
},
{
"ref": "PAX-02"
}
]
},
"lineId": "L-A349D3520C1B",
"market": "IT",
"status": "booked",
"tripId": "TRP-976512C541CF",
"channel": "web",
"groupId": null,
"currency": "EUR",
"updatedAt": "2026-11-02T09:06:42.000Z",
"attributes": {
"poType": "standard",
"source": "order_orchestration",
"offerId": "ALL-INCLUSIVE-2027",
"quantity": 1,
"poSequence": 1,
"departureAt": "2027-04-12T17:00:00Z",
"externalRef": "RSV-8CA144DF-000002",
"orderVersion": 4,
"promotionIds": [
"EARLY-BOOKING-10"
],
"quoteLineRef": "q-line-1",
"sourceSystem": "reservation",
"billingStatus": "not_billed",
"paymentStatus": "not_due",
"reservationRef": "RSV-8CA144DF-000002",
"tripCustomerRef": "CUST-20533",
"fulfilmentStatus": "confirmed"
},
"productRef": "MED7-2027-04-12",
"amountMinor": 238000,
"categoryRef": "BALCONY-B2",
"lineVersion": 3,
"productKind": "cruise",
"departureRef": "MED7-2027-04-12"
}
],
"after": [
{
"poId": "PO-B3A0355DEB58",
"roles": {
"travellers": [
{
"ref": "PAX-01"
},
{
"ref": "PAX-02"
}
]
},
"lineId": "L-A349D3520C1B",
"market": "IT",
"status": "booked",
"tripId": "TRP-976512C541CF",
"channel": "web",
"groupId": null,
"currency": "EUR",
"updatedAt": "2026-11-02T09:31:31.000Z",
"attributes": {
"poType": "standard",
"source": "order_orchestration",
"offerId": "ALL-INCLUSIVE-2027",
"quantity": 1,
"poSequence": 1,
"departureAt": "2027-04-12T17:00:00Z",
"externalRef": "RSV-8CA144DF-000002",
"orderVersion": 4,
"promotionIds": [
"EARLY-BOOKING-10"
],
"quoteLineRef": "q-line-1",
"sourceSystem": "reservation",
"billingStatus": "not_billed",
"paymentStatus": "not_due",
"reservationRef": "RSV-8CA144DF-000002",
"reAccommodation": {
"upgradedTo": null,
"disruptionId": "dis_6aa95d68-a42d-4d47-8170-95390ba977c3",
"fromProductRef": "MED7-2027-04-12",
"protectionRule": "same_price",
"fromCategoryRef": "BALCONY-B2",
"fromDepartureRef": "MED7-2027-04-12",
"protectionRuleId": "same-price",
"originalAmountMinor": 238000
},
"tripCustomerRef": "CUST-20533",
"fulfilmentStatus": "confirmed"
},
"productRef": "MED7-2027-04-12",
"amountMinor": 238000,
"categoryRef": "BALCONY-B2",
"lineVersion": 4,
"productKind": "cruise",
"departureRef": "MED7-2027-04-19"
}
],
"outcome": {
"rule": "same_price",
"lines": {
"L-A349D3520C1B": {
"amountMinor": 238000,
"explanation": [
{
"flag": "individual",
"mode": "select",
"basis": "booking",
"input": 0,
"layer": "target_price",
"reason": {
"key": "price_protection.target_price",
"params": {
"category": "BALCONY-B2",
"departure": "MED7-2027-04-19",
"travellers": 2
}
},
"ruleId": null,
"source": {
"kind": "runtime",
"condition": "target:MED7-2027-04-19"
},
"contribution": 252000,
"runningTotal": 252000
},
{
"flag": "individual",
"mode": "replace",
"basis": "booking",
"input": 252000,
"layer": "price_protection",
"reason": {
"key": "price_protection.same_price",
"params": {
"target": 252000,
"original": 238000
}
},
"ruleId": "same-price",
"source": {
"kind": "runtime",
"condition": "disruption:dis_6aa95d68-a42d-4d47-8170-95390ba977c3"
},
"contribution": -14000,
"runningTotal": 238000
}
],
"targetMinor": 252000,
"originalMinor": 238000
}
},
"ruleId": "same-price",
"priceDeltaMinor": 0
},
"updatedAt": "2026-11-02T09:31:31.000Z"
},
{
"changeId": "mc_510291cb-9863-4349-b300-eaaf0d44f0d6",
"tripId": "TRP-8232B7FF83C4",
"status": "applied",
"attempts": 1,
"error": null,
"plan": {
"lines": [
{
"lineId": "L-0A54E44D0375",
"expectedVersion": 4
}
]
},
"before": [
{
"poId": "PO-25CE761780FC",
"roles": {
"travellers": [
{
"ref": "PAX-01"
},
{
"ref": "PAX-02"
}
]
},
"lineId": "L-0A54E44D0375",
"market": "IT",
"status": "booked",
"tripId": "TRP-8232B7FF83C4",
"channel": "web",
"groupId": null,
"currency": "EUR",
"updatedAt": "2026-11-02T09:06:42.000Z",
"attributes": {
"poType": "standard",
"source": "order_orchestration",
"offerId": "ALL-INCLUSIVE-2027",
"quantity": 1,
"poSequence": 1,
"departureAt": "2027-04-12T17:00:00Z",
"externalRef": "RSV-078AC95B-000003",
"orderVersion": 4,
"promotionIds": [
"EARLY-BOOKING-10"
],
"quoteLineRef": "q-line-1",
"sourceSystem": "reservation",
"billingStatus": "not_billed",
"paymentStatus": "not_due",
"reservationRef": "RSV-078AC95B-000003",
"tripCustomerRef": "CUST-20617",
"fulfilmentStatus": "confirmed"
},
"productRef": "MED7-2027-04-12",
"amountMinor": 238000,
"categoryRef": "BALCONY-B2",
"lineVersion": 4,
"productKind": "cruise",
"departureRef": "MED7-2027-04-12"
}
],
"after": [
{
"poId": "PO-25CE761780FC",
"roles": {
"travellers": [
{
"ref": "PAX-01"
},
{
"ref": "PAX-02"
}
]
},
"lineId": "L-0A54E44D0375",
"market": "IT",
"status": "booked",
"tripId": "TRP-8232B7FF83C4",
"channel": "web",
"groupId": null,
"currency": "EUR",
"updatedAt": "2026-11-02T09:31:31.000Z",
"attributes": {
"poType": "standard",
"source": "order_orchestration",
"offerId": "ALL-INCLUSIVE-2027",
"quantity": 1,
"poSequence": 1,
"departureAt": "2027-04-12T17:00:00Z",
"externalRef": "RSV-078AC95B-000003",
"orderVersion": 4,
"promotionIds": [
"EARLY-BOOKING-10"
],
"quoteLineRef": "q-line-1",
"sourceSystem": "reservation",
"billingStatus": "not_billed",
"paymentStatus": "not_due",
"reservationRef": "RSV-078AC95B-000003",
"reAccommodation": {
"upgradedTo": null,
"disruptionId": "dis_6aa95d68-a42d-4d47-8170-95390ba977c3",
"fromProductRef": "MED7-2027-04-12",
"protectionRule": "same_price",
"fromCategoryRef": "BALCONY-B2",
"fromDepartureRef": "MED7-2027-04-12",
"protectionRuleId": "same-price",
"originalAmountMinor": 238000
},
"tripCustomerRef": "CUST-20617",
"fulfilmentStatus": "confirmed"
},
"productRef": "MED7-2027-04-12",
"amountMinor": 238000,
"categoryRef": "BALCONY-B2",
"lineVersion": 5,
"productKind": "cruise",
"departureRef": "MED7-2027-04-19"
}
],
"outcome": {
"rule": "same_price",
"lines": {
"L-0A54E44D0375": {
"amountMinor": 238000,
"explanation": [
{
"flag": "individual",
"mode": "select",
"basis": "booking",
"input": 0,
"layer": "target_price",
"reason": {
"key": "price_protection.target_price",
"params": {
"category": "BALCONY-B2",
"departure": "MED7-2027-04-19",
"travellers": 2
}
},
"ruleId": null,
"source": {
"kind": "runtime",
"condition": "target:MED7-2027-04-19"
},
"contribution": 252000,
"runningTotal": 252000
},
{
"flag": "individual",
"mode": "replace",
"basis": "booking",
"input": 252000,
"layer": "price_protection",
"reason": {
"key": "price_protection.same_price",
"params": {
"target": 252000,
"original": 238000
}
},
"ruleId": "same-price",
"source": {
"kind": "runtime",
"condition": "disruption:dis_6aa95d68-a42d-4d47-8170-95390ba977c3"
},
"contribution": -14000,
"runningTotal": 238000
}
],
"targetMinor": 252000,
"originalMinor": 238000
}
},
"ruleId": "same-price",
"priceDeltaMinor": 0
},
"updatedAt": "2026-11-02T09:31:31.000Z"
}
]
}
Errors
StatusCodeMeaning
400invalid_requestThe request failed validation. The message names the first failing field; oms-ops and group surfaces also return every issue in details.
401unauthenticatedThe bearer token is missing, invalid, expired or minted for another purpose.
403scope_forbiddenThe token is scoped to an agency or an access scope; this surface requires an unscoped tenant token.
404oms_ops_not_enabledOrder operations are not enabled for the tenant.
500internalUnexpected failure. Retry with the same Idempotency-Key.

Record a guest's choice of the alternative or a refund​

POST/oms-ops/mass-changes/{changeId}/items/{tripId}/guest-choice

Parameters

NameInRequiredNotes
changeIdpathyes
tripIdpathyes

Headers: Idempotency-Key, plus X-Request-Id.

Request body

FieldTypeRequiredNotes
actorobjectyes
actor.channelstringmax 60 chars
actor.idstringyes1–200 chars
actor.namestringmax 200 chars
actor.rolestringyes1–100 chars
choiceenum: accept, refundyes
notestringmax 2000 chars

Schema: apps/journeys/src/oms-ops/contracts.ts (GuestChoiceSchema)

Response 200

Errors
StatusCodeMeaning
400invalid_requestThe request failed validation. The message names the first failing field; oms-ops and group surfaces also return every issue in details.
401unauthenticatedThe bearer token is missing, invalid, expired or minted for another purpose.
403scope_forbiddenThe token is scoped to an agency or an access scope; this surface requires an unscoped tenant token.
404item_not_foundThe mass change has no item for this trip.
404oms_ops_not_enabledOrder operations are not enabled for the tenant.
409not_offeredThe request or offer is not in the offered state.
409choice_already_madeThe guest has already chosen.
409choice_window_closedThe guest-choice window has closed.
409idempotency_in_progressA request with this Idempotency-Key is still running. Retry after the Retry-After interval.
422idempotency_key_reusedThe Idempotency-Key was used for a different request.
500internalUnexpected failure. Retry with the same Idempotency-Key.

Retry failed items​

POST/oms-ops/mass-changes/{changeId}/retry

Parameters

NameInRequiredNotes
changeIdpathyes

Headers: Idempotency-Key, plus X-Request-Id.

Request body

FieldTypeRequiredNotes
actorobjectyes
actor.channelstringmax 60 chars
actor.idstringyes1–200 chars
actor.namestringmax 200 chars
actor.rolestringyes1–100 chars

Response 200

Errors
StatusCodeMeaning
400invalid_requestThe request failed validation. The message names the first failing field; oms-ops and group surfaces also return every issue in details.
401unauthenticatedThe bearer token is missing, invalid, expired or minted for another purpose.
403scope_forbiddenThe token is scoped to an agency or an access scope; this surface requires an unscoped tenant token.
404oms_ops_not_enabledOrder operations are not enabled for the tenant.
409nothing_to_retryThe mass change has no failed items to retry.
409idempotency_in_progressA request with this Idempotency-Key is still running. Retry after the Retry-After interval.
422idempotency_key_reusedThe Idempotency-Key was used for a different request.
500internalUnexpected failure. Retry with the same Idempotency-Key.

Roll back a mass change​

POST/oms-ops/mass-changes/{changeId}/rollback

Parameters

NameInRequiredNotes
changeIdpathyes

Headers: Idempotency-Key, plus X-Request-Id.

Request body

FieldTypeRequiredNotes
actorobjectyes
actor.channelstringmax 60 chars
actor.idstringyes1–200 chars
actor.namestringmax 200 chars
actor.rolestringyes1–100 chars
reasonstringyesmin 1 chars

Response 200

POST/oms-ops/mass-changes/mc_510291cb-9863-4349-b300-eaaf0d44f0d6/rollbackRoll the change back
Request
curl -X POST "$BASE/oms-ops/mass-changes/mc_510291cb-9863-4349-b300-eaaf0d44f0d6/rollback" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Request-Id: req-0046" \
-H "Content-Type: application/json" \
-d '{
"actor": {
"id": "ops-0007",
"name": "Paolo Ricci",
"role": "ops_manager",
"channel": "back_office"
},
"reason": "Departure reinstated after repair"
}'
Response
HTTP/1.1 200
X-Request-Id: req-0046
{
"original": {
"kind": "re_accommodation",
"spec": {
"kind": "re_accommodation",
"protection": {
"ruleId": "same-price"
},
"targetPrices": {
"BALCONY-B2": {
"amountMinor": 252000,
"perTraveller": false
}
},
"categoryMapping": {},
"targetDepartureRef": "MED7-2027-04-19"
},
"actor": {
"id": "ops-0007",
"name": "Paolo Ricci",
"role": "ops_manager",
"channel": "back_office"
},
"counts": {
"total": 3,
"pending": 0,
"applied": 0,
"failed": 0,
"needs_person": 0,
"skipped": 0,
"rolled_back": 3,
"rollback_conflict": 0
},
"reason": "Move affected guests to the next departure",
"status": "rolled_back",
"changeId": "mc_510291cb-9863-4349-b300-eaaf0d44f0d6",
"tenantId": "meridian",
"createdAt": "2026-11-02T09:30:54.000Z",
"startedAt": "2026-11-02T09:31:31.000Z",
"updatedAt": "2026-11-02T09:30:54.000Z",
"finishedAt": "2026-11-02T09:31:31.000Z",
"population": {
"lines": 3,
"trips": 3
},
"rollbackOf": null,
"commitToken": "3f1d37d6df1a7b076339f1f93da107f4",
"disruptionId": "dis_6aa95d68-a42d-4d47-8170-95390ba977c3",
"rolledBackBy": "mc_2398b4e3-7636-4cec-b823-19c6ba76c7d7",
"throughputPerSecond": 3000,
"contact": {
"requested": 8,
"trips": 3
}
},
"rollback": {
"kind": "rollback",
"spec": {
"kind": "rollback"
},
"actor": {
"id": "ops-0007",
"name": "Paolo Ricci",
"role": "ops_manager",
"channel": "back_office"
},
"counts": {
"total": 3,
"failed": 0,
"applied": 0,
"pending": 0,
"skipped": 0,
"rolled_back": 3,
"needs_person": 0,
"rollback_conflict": 0
},
"reason": "Departure reinstated after repair",
"status": "completed",
"contact": {
"trips": 3,
"requested": 8
},
"changeId": "mc_2398b4e3-7636-4cec-b823-19c6ba76c7d7",
"tenantId": "meridian",
"createdAt": "2026-11-02T09:33:22.000Z",
"startedAt": "2026-11-02T09:33:22.000Z",
"updatedAt": "2026-11-02T09:33:22.000Z",
"finishedAt": "2026-11-02T09:33:22.000Z",
"population": {
"lines": 3,
"trips": 3
},
"rollbackOf": "mc_510291cb-9863-4349-b300-eaaf0d44f0d6",
"commitToken": "",
"disruptionId": "dis_6aa95d68-a42d-4d47-8170-95390ba977c3",
"rolledBackBy": null,
"throughputPerSecond": 3000
}
}
Errors
StatusCodeMeaning
400invalid_requestThe request failed validation. The message names the first failing field; oms-ops and group surfaces also return every issue in details.
400reason_requiredThis action requires a reason.
401unauthenticatedThe bearer token is missing, invalid, expired or minted for another purpose.
403scope_forbiddenThe token is scoped to an agency or an access scope; this surface requires an unscoped tenant token.
404oms_ops_not_enabledOrder operations are not enabled for the tenant.
409already_rolled_backThe mass change has already been rolled back.
409idempotency_in_progressA request with this Idempotency-Key is still running. Retry after the Retry-After interval.
422idempotency_key_reusedThe Idempotency-Key was used for a different request.
500internalUnexpected failure. Retry with the same Idempotency-Key.