Create Voyage Booking
See Booking flow for the end-to-end lifecycle.
Endpoint: /bookings/create/voyage-only/{:sessionId}
METHOD: POST
Creates a held booking for the chosen voyage within an existing voyage-only search session. Reserves the selected cabin(s) and any vehicle slots requested at search time, and surfaces any mandatory addons that are automatically included.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| sessionId | string (UUID) | Yes | Session returned by /search/voyages. Must be voyage-only. |
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| user_key | string | Yes | Unique key associated with the calling agency. |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
departureDate |
string (date-time) | Yes | Voyage departure timestamp. Must match results[].departureDate from the originating search and be in the future. |
cabinMix |
array of objects | Yes | One entry per cabin to reserve. Total guestIds across cabins must not exceed 9, must all be unique, and must each appear as a guest in the search. |
cabinMix[].code |
string | Yes | Cabin grade code from the search response. Must be available on the selected voyage. |
cabinMix[].guestIds |
array of numbers | Yes | Passenger IDs (from the search) assigned to this cabin. Must contain at least one passenger of type: "ADULT" or type: "SENIOR" and not exceed the grade's maximumOccupancy. |
Example
Request
curl --location '{:apiBase}/bookings/create/voyage-only/{:sessionId}?user_key=abcdef' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer XXXXXXXX' \
--data '{
"departureDate": "2026-05-11T18:15:00",
"cabinMix": [
{
"code": "DECK",
"guestIds": [1]
}
]
}'
Response
{
"vehicles": [
{
"type": "CAR",
"guestId": 1
}
],
"addons": [
{
"code": "WIFI",
"guests": [1],
"isMandatory": true
}
],
"excursions": [],
"supplementalTours": [],
"priceBreakdown": {
"totalPrice": 249.0,
"cabins": [
{
"code": "DECK",
"guests": [
{
"guestId": 1,
"price": 106.0
}
]
}
],
"addons": [
{
"code": "WIFI",
"guests": [
{
"guestId": 1,
"price": 70
}
]
}
],
"excursions": [],
"supplementalTours": [],
"vehicles": [
{
"code": "CAR",
"guest": {
"guestId": 1,
"price": 73.0
}
}
]
},
"sessionId": "f91c02b2-f22a-42c9-80b7-e3ef591ef0e0",
"market": "US",
"currency": "USD",
"departureDate": "2026-05-11T18:15:00",
"arrivalDate": "2026-05-12T10:55:00",
"departurePort": "TOS",
"arrivalPort": "HVG",
"shipCode": "KH",
"embarkationNotification": [
{
"locale": "en",
"value": "Boarding opens 60 minutes before departure. Please be at the terminal in good time."
},
{
"locale": "nb-NO",
"value": "Ombordstigning åpner 60 minutter før avgang. Vær ved terminalen i god tid."
}
],
"guests": [
{
"id": 1,
"type": "ADULT"
}
],
"cabins": [
{
"id": 1,
"code": "DECK",
"cabinType": "Inside",
"cabinNumber": null,
"guests": [
1
]
}
]
}
Fields
| Field | Type | Description |
|---|---|---|
sessionId |
string (UUID) | The booking session this held booking belongs to. |
market |
string | Market the booking was created under. |
currency |
string | Currency the booking is priced in. |
departureDate |
string (date-time) | Voyage departure timestamp. |
arrivalDate |
string (date-time) | Voyage arrival timestamp. |
departurePort |
string | Departure port code. |
arrivalPort |
string | Arrival port code. |
shipCode |
string | Ship code (see /refdata/ships). |
embarkationNotification |
array of objects | Localized embarkation instruction for the departure port: [{ locale, value }]. null when none applies to this departure. |
guests |
array of objects | Passengers on the booking. |
guests[].id |
number | Passenger ID (matches the IDs used in cabinMix[].guestIds). |
guests[].type |
string | One of ADULT, CHILD, INFANT, SENIOR. |
cabins |
array of objects | Cabins reserved. |
cabins[].id |
number | Identifies this cabin on the booking. Send it as cabinId when choosing a cabin number. |
cabins[].code |
string | Cabin grade code. |
cabins[].cabinType |
string | Cabin type (e.g. Inside, Outside, Suite). Categorization above the per-ship cabin grade. |
cabins[].cabinNumber |
string | The specific cabin allocated, once one has been chosen via /cabin-numbers. null while the booking holds a guarantee cabin, where the cabin is assigned before departure. |
cabins[].guests |
array of numbers | Passenger IDs assigned to this cabin. |
vehicles |
array of objects | Vehicle slots reserved. Each vehicle is bound to a passenger; vehicles cannot be added to or removed from the booking after this point. |
vehicles[].type |
string | Vehicle category (CAR, MOTORCYCLE). |
vehicles[].guestId |
number | Passenger the vehicle is associated with. |
addons |
array of objects | Addons currently on the booking. Mandatory addons are auto-included here at create time and must not be re-applied. |
addons[].code |
string | Addon code (see /bookings/{:sessionId}/addons to discover available addons for the booking). |
addons[].guests |
array of numbers | Passenger IDs the addon applies to. |
addons[].isMandatory |
boolean | true for addons auto-included by Hurtigruten; these cannot be removed. |
excursions |
array of objects | Excursions currently on the booking. Always present; empty when nothing is applied. |
excursions[].code |
string | Excursion code (see /bookings/create/voyage-only/{:sessionId}/excursions). |
excursions[].startDate |
string (date-time) | Excursion start timestamp. |
excursions[].endDate |
string (date-time) | Excursion end timestamp. |
excursions[].isMandatory |
boolean | true when the excursion is bundled and cannot be individually removed. Currently always false on voyage-only bookings — non-removable excursions surface only on package bookings. |
excursions[].guests |
array of numbers | Passenger IDs the excursion applies to. |
supplementalTours |
array of objects | Supplemental tours (pre- or post-cruise extensions) currently on the booking. Always present; empty when nothing is applied. |
supplementalTours[].code |
string | Supplemental-tour code. |
supplementalTours[].startDate |
string (date-time) | Tour start timestamp. |
supplementalTours[].endDate |
string (date-time) | Tour end timestamp. |
supplementalTours[].tourCategory |
string | One of Arrival (pre-cruise) or Departure (post-cruise). |
supplementalTours[].isMandatory |
boolean | true when the tour cannot be removed. |
supplementalTours[].guests |
array of numbers | Passenger IDs the tour applies to. |
supplementalTours[].tourStageSelectableRouteOverrides |
array of objects | Per-stage route overrides chosen for the tour. Empty when no overrides apply. |
supplementalTours[].tourStageSelectableRouteOverrides[].tourStageId |
number | Tour-stage identifier. |
supplementalTours[].tourStageSelectableRouteOverrides[].departureCode |
string | Override departure port code. |
supplementalTours[].tourStageSelectableRouteOverrides[].arrivalCode |
string | Override arrival port code. |
priceBreakdown |
object | Price detail. Components sum to priceBreakdown.totalPrice. |
priceBreakdown.totalPrice |
number | Total price for the booking in the booking currency. |
priceBreakdown.cabins |
array of objects | Per-cabin pricing. |
priceBreakdown.cabins[].code |
string | Cabin grade code. |
priceBreakdown.cabins[].guests |
array of objects | Per-passenger price for this cabin. |
priceBreakdown.cabins[].guests[].guestId |
number | Passenger ID. |
priceBreakdown.cabins[].guests[].price |
number | Passenger's portion of the cabin price. |
priceBreakdown.addons |
array of objects | Per-addon pricing. |
priceBreakdown.addons[].code |
string | Addon code. |
priceBreakdown.addons[].guests |
array of objects | Per-passenger price for this addon. |
priceBreakdown.addons[].guests[].guestId |
number | Passenger ID. |
priceBreakdown.addons[].guests[].price |
number | Passenger's portion of the addon price. |
priceBreakdown.excursions |
array of objects | Per-excursion pricing. |
priceBreakdown.excursions[].code |
string | Excursion code. |
priceBreakdown.excursions[].guests |
array of objects | Per-passenger price for this excursion. |
priceBreakdown.excursions[].guests[].guestId |
number | Passenger ID. |
priceBreakdown.excursions[].guests[].price |
number | Passenger's portion of the excursion price. |
priceBreakdown.supplementalTours |
array of objects | Per-supplemental-tour pricing. |
priceBreakdown.supplementalTours[].code |
string | Supplemental-tour code. |
priceBreakdown.supplementalTours[].guests |
array of objects | Per-passenger price for this supplemental tour. |
priceBreakdown.supplementalTours[].guests[].guestId |
number | Passenger ID. |
priceBreakdown.supplementalTours[].guests[].price |
number | Passenger's portion of the supplemental-tour price. |
priceBreakdown.vehicles |
array of objects | Per-vehicle pricing. |
priceBreakdown.vehicles[].code |
string | Vehicle category. |
priceBreakdown.vehicles[].guest.guestId |
number | Passenger the vehicle is bound to. |
priceBreakdown.vehicles[].guest.price |
number | Vehicle price. |
Errors
See Error model for response-body shapes across status codes.
| Status | Cause |
|---|---|
| 400 | user_key query parameter is missing; the request body fails validation (rules listed below); or the request references a voyage, cabin grade, or passenger that is not in the session. |
| 404 | The session does not exist or has expired. |
| 409 | A held booking already exists in this session (each session can hold one booking at a time; start a new search for another booking); or the session does not match this endpoint's booking type. |
Validation rules. A 400 is returned when any of these rules are not met:
cabinMixmust be non-empty.- Each cabin's
codemust be non-empty andguestIdsmust be non-empty, with everyguestId > 0. - Total
guestIdsacross all cabins must not exceed 9, and everyguestIdmust be unique across the request. departureDatemust be in the future and must match a voyage's departure timestamp returned by the originating search.- Every
guestIdand every cabincodemust come from the search response. - Each cabin's
guestIdscount must not exceed the cabin grade'smaximumOccupancy, and at least one of those passengers must be oftype: "ADULT"ortype: "SENIOR". - Vehicles requested at search time must still be available on the selected voyage in the requested quantity.
Mandatory addons. If the booking includes mandatory addons (for example, on-board Wi-Fi), they appear in addons with isMandatory: true and are already priced into priceBreakdown.totalPrice. Do not call /bookings/{:sessionId}/addons for these — re-applying a mandatory addon is not supported.