Skip to main content

Create Booking Option

Add another way to sell an experience.

The new option lands at the bottom of the list. Use Reorder booking options to move it.

Endpointโ€‹

POST /v1/experiences/{id}/booking-options

Authenticationโ€‹

Requires an API key with the experiences:write scope. There is no separate booking-options scope.

Path Parametersโ€‹

ParameterTypeDescription
idintegerExperience ID

Request Bodyโ€‹

FieldTypeRequiredDescription
namestringYesWhat the shop calls this way of booking, for example Private session. 1 to 120 characters
price_structurestringNoparticipant (a price per person) or group (one price for the whole party). Defaults to participant
exclusivebooleanNotrue when booking this takes the whole session, so nobody else can join it. Defaults to false
min_peopleintegerNoSmallest party this option takes, at least 1. Omit or send null for no minimum of its own
max_peopleintegerNoLargest party this option takes, at least 1. Omit or send null for no limit of its own
default_groups_capacityintegerNoHow many groups one session sells under this option, at least 1. Omit or send null to use the experience's own number
unit_namestringNoWhat each group is under this option, for example canoe. Up to 40 characters. Omit or send null to follow the experience's unit_name
unit_name_pluralstringNoThe plural, when the worked-out one is wrong. Up to 40 characters. Omit it and canoe reads canoes
duration_valuenumberNoThis option's own length, greater than zero. Only accepted with duration_unit
duration_unitstringNominutes, hours or days. Only accepted with duration_value

Leave both duration fields out and the option runs for as long as the experience says.

Example Requestโ€‹

curl -X POST "https://app.equipdash.com/api/v1/experiences/1234567/booking-options" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Private session",
"price_structure": "group",
"exclusive": true,
"unit_name": null,
"unit_name_plural": null,
"min_people": 1,
"max_people": 4,
"duration_value": 90,
"duration_unit": "minutes"
}'

Responseโ€‹

Success (201 Created)โ€‹

{
"data": {
"id": 4322,
"experience_id": 1234567,
"name": "Private session",
"price_structure": "group",
"exclusive": true,
"unit_name": null,
"unit_name_plural": null,
"min_people": 1,
"max_people": 4,
"default_groups_capacity": null,
"duration": {
"value": 90,
"unit": "minutes"
},
"sort_order": 1,
"archived": false,
"archived_at": null,
"created_at": "2026-02-01T10:00:00+00:00",
"updated_at": "2026-02-01T10:00:00+00:00"
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2026-02-01T10:00:00+00:00"
}
}

Errorsโ€‹

StatusCodeDescription
401authentication_requiredNo API key provided
401invalid_api_keyInvalid API key
403insufficient_scopeAPI key lacks experiences:write scope
403forbiddenExperience belongs to a different supplier
404resource_not_foundExperience does not exist
422validation_errorInvalid request body

Validation Rulesโ€‹

  • name is required and must be 1 to 120 characters
  • price_structure must be participant or group
  • min_people and max_people must each be at least 1, and min_people cannot be greater than max_people
  • duration_value must be greater than zero, and needs duration_unit with it. Sending one without the other is refused
  • default_groups_capacity must be at least 1. A session that takes no groups could never be booked
Where the message lands

EquipDash checks these rules in the one place every surface writes through, so the portal, the point of sale and this API all refuse the same request. Those messages come back under the field name in error.details, whatever the field actually at fault was. Read the message, not the field name:

{
"error": {
"code": "validation_error",
"message": "The given data was invalid.",
"details": [
{ "field": "name", "message": "The fewest people cannot be more than the most people." }
]
},
"meta": { "request_id": "req_abc123" }
}

Notesโ€‹

  • Adding a second live option is what tells the shop's booking page to offer a choice. While an experience has one option, no chooser appears anywhere.
  • A new option has no price of its own until one is set for it. Give it one with Add pricing, naming this option's id as booking_option_id. A booking naming an option with no price is refused with 422 booking_creation_failed.
  • default_groups_capacity is a ceiling under exclusive, never an override of it. An option that books out the session takes the whole session however many groups this number allows.