Apply a Cabin Number (Voyage)
See Booking flow for the end-to-end lifecycle.
Endpoint: /bookings/create/voyage-only/{:sessionId}/cabin-numbers
METHOD: POST
Books specific cabins on a held booking, replacing the guarantee cabins it was created with.
Call List Available Cabin Numbers first — a cabin number that has not been offered for that grade is rejected. Apply this before confirming; once the booking is confirmed the cabins are fixed.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| sessionId | string (UUID) | Yes | Session returned by /search/voyages. Must be voyage-only and hold a booking. |
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| user_key | string | Yes | Unique key associated with the calling agency. |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
cabins |
array of objects | Yes | One entry per cabin to assign. Cabins left out keep whatever they already hold. |
cabins[].cabinId |
number | Yes | id of the cabin on the booking response. Each cabin can appear at most once. |
cabins[].cabinNumber |
string | No | Cabin number from the available list for that cabin's grade, or null to release the cabin back to a guarantee. Omitting the field is the same as sending null. Matched case-insensitively; surrounding whitespace is ignored. The same number cannot be requested for two cabins. An empty or blank string is rejected — send null. |
Example
Request
curl --location '{:apiBase}/bookings/create/voyage-only/{:sessionId}/cabin-numbers?user_key=abcdef' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer XXXXXXXX' \
--data '{
"cabins": [
{
"cabinId": 1,
"cabinNumber": "313"
}
]
}'
Response
{
"vehicles": [],
"addons": [],
"excursions": [],
"supplementalTours": [],
"priceBreakdown": {
"totalPrice": 249.0,
"cabins": [
{
"code": "O3",
"guests": [
{
"guestId": 1,
"price": 249.0
}
]
}
],
"addons": [],
"excursions": [],
"supplementalTours": [],
"vehicles": []
},
"sessionId": "f91c02b2-f22a-42c9-80b7-e3ef591ef0e0",
"market": "NO",
"currency": "NOK",
"departureDate": "2026-11-02T20:30:00",
"arrivalDate": "2026-11-08T09:00:00",
"departurePort": "BGO",
"arrivalPort": "KKN",
"shipCode": "NL",
"embarkationNotification": null,
"guests": [
{
"id": 1,
"type": "ADULT"
}
],
"cabins": [
{
"id": 1,
"code": "O3",
"cabinType": "Outside",
"cabinNumber": "313",
"guests": [
1
]
}
]
}
Fields
The response body is identical in shape to Create Voyage Booking — see its Fields table for the full field reference. cabins[].cabinNumber now carries the allocated cabin.
Errors
See Error model for response-body shapes across status codes.
| Status | Cause |
|---|---|
| 400 | user_key is missing; the body fails validation (see below); a cabinId is not a cabin on the booking; the available numbers for that cabin's grade have not been listed in this session; a cabinNumber was not among them; or the cabin's grade does not correspond to specific cabins. |
| 404 | The session does not exist or has expired. |
| 409 | The session is not voyage-only; no held booking exists in it yet; or the cabin could not be secured. |
Validation rules. A 400 is returned when any of these are not met:
cabinsmust be non-empty.- Each
cabinIdmust address a cabin on the booking. - No cabin may be given two numbers, and no number may be requested for two cabins.
- Each
cabinNumbermust either benull, or appear in the available list for that cabin's grade. Blank strings are rejected.
Releasing a cabin. Send null as the cabinNumber to give a chosen cabin back and return to a guarantee cabin, assigned before departure. No prior call to List Available Cabin Numbers is needed to release, and the response carries cabinNumber: null for that cabin. Changing directly from one cabin to another needs no release first — apply the new number.
List before every apply. The available numbers are recorded against the session when you call List Available Cabin Numbers, and are discarded once an apply succeeds. So each POST must be preceded by a GET for every grade it references, in the same session — including when you come back to assign a second cabin or change an earlier choice. Assigning cabins across two grades in one POST means calling the GET once per grade first, and those GETs must be made one at a time. A retried POST whose first attempt actually succeeded returns 400 for the same reason — read the booking back with Get Held Booking to see which cabin is held before listing again.
When a cabin cannot be secured. Availability is shared with every other booking, so a cabin listed a moment ago may already have been taken. That returns 409 with the booking unchanged, and the remedy is to list the available numbers again and choose another cabin. Nothing is silently substituted — whenever this call succeeds, cabins[].cabinNumber in the response is the cabin you hold.