Cart items are line items inside a cart — bookings, stays, tickets, and passes. The API currently supports creating and removing cart items.
Unsupported operations
Some route definitions may suggest additional operations (like "new" or "update"), but they are not implemented as part of the public API contract.
If you attempt an unsupported operation, you should expect a 404 or similar failure.
All endpoints are location- and cart-scoped:
/store/v0/carts/{cart_public_id}
Add item to cart
POST /store/v0/carts/{cart_public_id}/itemsRequest body
{
"cart_item": {
"type": "booking",
"attributes": {}
}
}type is one of booking, stay, ticket or pass, and determines which attributes are required. Any other value returns a 400 (see Errors).
Changes on 4 November 2026
Bookings used to be called appointments. Until 4 November 2026, "type": "appointment" still adds a booking, and the response carries Deprecation and Sunset headers. On that date it returns a 400, so send "booking" before then.
On the same date a booking's kind in the cart changes from "appointment" to "booking". Handle both values before then, so your storefront keeps working when it changes. A service's own kind (appointment or class) does not change.
Booking cart item
Use this for bookings ("type": "booking").
Request body
{
"cart_item": {
"type": "booking",
"attributes": {
"service_id": "9c1a2b3c-4d5e-4f60-8123-9a0b1c2d3e4f",
"service_variant_id": "4a7b8c9d-0e1f-4a2b-9c3d-4e5f6a7b8c9d",
"starts_at": "2026-03-20T08:30:00+01:00",
"guest_count": 2,
"preferred_staff_resource_id": "7f6e5d4c-3b2a-4c1d-9e0f-1a2b3c4d5e6f"
}
}
}Attributes
| Attribute | Type | Required | Description |
|---|---|---|---|
service_id | string | Yes | Service public ID |
service_variant_id | string | Yes | Service variant public ID |
starts_at | string | Yes | ISO 8601 timestamp for the slot start |
guest_count | integer | No | Number of guests for this booking. Defaults to 1 |
preferred_staff_resource_id | string | No | Optional; only meaningful if service allows customer resource preference |
Guest count and shared services
The guest_count attribute controls how many guests are included in the booking. This is particularly important for shared services (e.g., a sauna session where multiple guests can book the same time slot).
- Pricing: The cart item price is calculated as
unit_price x guest_count. For example, a service variant priced at 250 NOK withguest_count: 4results in a subtotal of 1,000 NOK. - Quantity: The cart item's
quantityfield in the response reflects theguest_countvalue. - Capacity: The
guest_countmust not exceed the slot'savailable_capacityas reported by the Service availability endpoint. If the requestedguest_countexceeds available capacity, the API returns a422.
For exclusive services (where only one booking occupies a slot), guest_count still controls pricing but the slot is fully reserved regardless of the count.
Note: The attribute name is
guest_count, notparticipant_count. Unrecognized attributes are silently ignored, which means sendingparticipant_countwill result in aguest_countof 1.
Important
starts_atmust correspond to a currently bookable slot from Service availability.- If availability changes between fetching slots and adding to cart, you may get a
422. - Always check the slot's
available_capacitybefore choosing aguest_countvalue to avoid rejected requests.
Stay cart item
Use this for accommodation bookings (stays).
Request body
{
"cart_item": {
"type": "stay",
"attributes": {
"accommodation_id": "2f1b2d7c-6a51-4d35-9b5a-9b0c3b8efb2a",
"check_in_date": "2026-02-01",
"check_out_date": "2026-02-05",
"occupancy": {
"adults": 2,
"children": 1,
"infants": 0
}
}
}
}Attributes
| Attribute | Type | Required | Description |
|---|---|---|---|
accommodation_id | string | Yes | Accommodation public ID |
check_in_date | string | Yes | YYYY-MM-DD |
check_out_date | string | Yes | YYYY-MM-DD |
occupancy | object | No | See Types |
Ticket cart item
Use this for event ticket purchases.
Request body
{
"cart_item": {
"type": "ticket",
"attributes": {
"ticket_variant_id": "e5f6a7b8-c9d0-4e1f-8a2b-3c4d5e6f7a8b",
"quantity": 2
}
}
}Attributes
| Attribute | Type | Required | Description |
|---|---|---|---|
ticket_variant_id | string | Yes | Ticket variant public ID |
quantity | integer | No | Defaults to 1 |
Pass cart item
Use this for pass purchases (prepaid punch cards).
Request body
{
"cart_item": {
"type": "pass",
"attributes": {
"pass_product_id": "8b2c3d4e-5f60-4a71-9234-0b1c2d3e4f5a",
"quantity": 1
}
}
}Attributes
| Attribute | Type | Required | Description |
|---|---|---|---|
pass_product_id | string | Yes | Pass public ID (from the Passes endpoint) |
quantity | integer | No | Number of passes. Defaults to 1 |
Adding the same pass again merges into the existing cart item by increasing its quantity. Only public passes can be added — draft or private passes return a 404.
Response
On success, the API returns the updated cart:
- Status:
201 Created - Body:
{ "cart": { ... } }
Remove item from cart
DELETE /store/v0/carts/{cart_public_id}/items/{public_id}On success, returns the updated cart.
Removing an item that belongs to a package removes the whole package and all its items. See Cart packages.
Cart item object reference (common fields)
Cart items include a set of common fields:
| Field | Type | Description |
|---|---|---|
public_id | string | Cart item identifier |
kind | string | "appointment", "stay", "ticket", or "pass"; a package can also add "product" items |
title | string | Display title |
summary | string | Brief summary |
quantity | integer | Quantity |
starts_at | string | ISO 8601 timestamp (when applicable) |
ends_at | string | ISO 8601 timestamp (when applicable) |
thumbnail | object | null | Media |
prices | object | Price breakdown |
package_id | string | null | The cart package this item belongs to |
Booking fields
When kind is "appointment" ("booking" from 4 November 2026):
| Field | Type | Description |
|---|---|---|
service_name | string | Service name |
service_variant_name | string | Variant name |
guest_count | integer | Number of guests (determines quantity and pricing) |
Stay-specific fields
When kind is "stay":
| Field | Type | Description |
|---|---|---|
accommodation_name | string | Accommodation name |
duration_nights | integer | Nights |
occupancy | object | Occupancy |
Ticket-specific fields
When kind is "ticket":
| Field | Type | Description |
|---|---|---|
event_name | string | Event name |
ticket_variant_name | string | Ticket variant name |
Pass-specific fields
When kind is "pass":
| Field | Type | Description |
|---|---|---|
pass_product_id | string | Pass public ID |
pass_product_name | string | Pass name |
unit_count | integer | Units per pass |