Skip to main content

Get Experience

Retrieve a single experience by its ID.

Endpointโ€‹

GET /v1/experiences/{id}

Authenticationโ€‹

Requires an API key with the experiences:read scope.

Parametersโ€‹

ParameterTypeLocationDescription
idintegerpathThe experience ID
langstringqueryReturn translated content fields (name, brief_description, long_description, content blocks) in the given language, e.g. fr. Only applies when the supplier has that booking widget language enabled โ€” otherwise, and for any field without a translation yet, the English value is returned. Defaults to en.

Responseโ€‹

Success (200 OK)โ€‹

{
"data": {
"id": 1,
"name": "Sunset Kayak Tour",
"unique_code": "SKT-001",
"status": "active",
"list_as_experience": true,
"staff_only": false,
"shares_places_with_id": null,
"who_can_book": "everyone",
"suggest_in_cart": true,
"booking_mode": "instant",
"brief_description": "2-hour guided kayak tour at sunset",
"long_description": "Experience the beauty of the coast as the sun sets over the water...",
"category": {
"id": 2,
"name": "Water Activities"
},
"duration": {
"value": 2,
"unit": "hours"
},
"multi_day": false,
"pricing": {
"applies": "yearRound",
"structure": "participant",
"unit_name": null,
"unit_name_plural": null,
"asks_headcount": true
},
"capacity": 12,
"location": "Marina Bay",
"booking_notice": {
"required": true,
"value": 24,
"unit": "Hours",
"minutes": 1440,
"same_day_cutoff_time": "08:30",
"inherited": false
},
"participants_required": true,
"participants_required_override": true,
"required_certifications": ["first_aid", "lifeguard"],
"required_certification_names": ["First Aid", "Lifeguard"],
"required_languages": ["es"],
"required_language_names": ["Spanish"],
"equipment": [
{ "product_id": 41, "units_basis": "participant", "units_per": 1, "rate_category_id": null, "ask_size": true },
{ "product_id": 43, "units_basis": "session", "units_per": 1, "rate_category_id": null, "ask_size": false }
],
"asks_each_person": [
{
"field_id": 87,
"label": "Wetsuit size",
"answer_type": "select",
"is_size": true,
"product_id": 41,
"choices": ["S", "M", "L"],
"required": true,
"checked_at_checkin": false
},
{
"field_id": 88,
"label": "Can you swim 50m?",
"answer_type": "yes_no",
"is_size": false,
"product_id": null,
"choices": [],
"required": true,
"checked_at_checkin": true
}
],
"availability": [
{
"id": 1,
"day_of_week": "monday",
"start_time": "17:00:00",
"end_time": "19:00:00",
"capacity": 12
}
],
"pricing_rules": [
{
"id": 1,
"season_id": null,
"rate_category_id": 1,
"price": 65.00,
"price_per_participant": null,
"per_participant_included": null,
"max_participants": null
},
{
"id": 2,
"season_id": null,
"rate_category_id": 2,
"price": 45.00,
"price_per_participant": null,
"per_participant_included": null,
"max_participants": null
}
],
"booking_options": [
{
"id": 4321,
"experience_id": 1,
"name": "Group class",
"price_structure": "participant",
"exclusive": false,
"min_people": null,
"max_people": null,
"duration": { "value": null, "unit": null },
"sort_order": 0,
"archived": false,
"archived_at": null,
"created_at": "2026-01-01T00:00:00+00:00",
"updated_at": "2026-01-01T00:00:00+00:00"
}
],
"images": [
{
"id": 5,
"url": "https://example.com/images/kayak-1.jpg",
"is_default": true,
"position": 0
}
],
"default_image": {
"id": 5,
"url": "https://example.com/images/kayak-1.jpg"
},
"custom_fields": [
{
"field_id": 3,
"label": "Difficulty Level",
"field_type": "text",
"value": "Intermediate",
"formatted_value": "Intermediate"
}
],
"content_blocks": {
"whats_included": [{"label": "Kayak + paddle", "description": null}],
"does_not_include": [{"label": "Meals", "description": null}],
"itinerary": null,
"highlights": ["Sunset views", "Small groups"],
"check_in_details": "<p>Meet at the <strong>Marina Bay kiosk</strong> 15 min early.</p>",
"what_to_bring": ["Swimwear", "Sunscreen"],
"restrictions": "<p>Minimum age 10.</p>",
"health_and_safety": "<p>Life vests provided.</p>",
"cancellation_notes": "<p>Free cancellation up to 24 hours before.</p>",
"custom_sections": [
{ "heading": "Weather policy", "body": "<p>Tours run rain or shine; we only cancel for <strong>lightning</strong>.</p>" }
]
},
"channels": [
{
"channel": "getyourguide",
"name": "GetYourGuide",
"state": "live",
"status": "Live",
"product_id": "1",
"taking_bookings": true,
"last_booking_at": "2026-01-12T09:14:00+00:00",
"problem": null
},
{
"channel": "viator",
"name": "Viator",
"state": "ready",
"status": "Ready",
"product_id": "EQD-EXP-1",
"taking_bookings": true,
"last_booking_at": null,
"problem": null
}
],
"created_at": "2026-01-01T00:00:00+00:00",
"updated_at": "2026-01-15T10:30:00+00:00"
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2026-01-15T12:00:00+00:00"
}
}

Response Fieldsโ€‹

See List Experiences for complete field descriptions.

Booking Optionsโ€‹

booking_options is the ways this experience can be sold, in the shop's display order. Every experience has at least one, so a shop that sells its experience one way gets a single entry and nothing else about this response changes. Only this endpoint carries the field; List Experiences leaves it out.

FieldTypeDescription
booking_optionsarrayBooking option objects. See The Booking Option Object

Two things to know before you render a chooser from it:

  • Archived options are included, so a client showing an existing booking can still name the option it was sold as. Filter on archived before offering a choice.
  • The option's own values win over the pricing.structure and is_private fields, which describe the experience as a whole. Read price_structure on the option for how it is priced, and exclusive for whether anyone else can join.

What each group isโ€‹

pricing.unit_name is what each group is when the experience is priced per unit ("canoe"), and null for plain groups or per-person pricing. pricing.unit_name_plural is the owner's own plural or the worked-out one ("canoes"), and is never null while unit_name is set. Each booking option in booking_options carries the same two fields; a null there follows the experience. See Priced per unit.

pricing.asks_headcount is false when a booking needs only the number of units: the experience is priced per unit, its people per unit is empty (no limit), and there is no per-person charge or per-person details. Send just quantity (the units); the booking counts one person per unit. When true, send the headcount as usual.

Gear and what each person is askedโ€‹

equipment is the gear the experience uses, one row per item. ask_size is true when checkout asks each person their size of that item. Turn it on or off with the same field on Update Experience; the item needs sizes first.

asks_each_person is every question checkout asks each person: item sizes first, then the shop's own questions (a certificate level, a weight). It is empty when the experience asks nothing. Each entry is a Question object. Manage the shop's own questions with the Questions endpoints.

Only this endpoint, Create Experience and Update Experience return the two fields; List Experiences leaves them out.

Marketplacesโ€‹

channels says where this experience is sold on a marketplace: one entry for each marketplace the shop has connected (GetYourGuide, Viator, Manawa), and an empty list when none is. It is the same answer the shop sees on the experience page. Read-only, and only this endpoint carries it; List Experiences leaves it out. It is part of the experience, so any key with experiences:read gets it. It never carries a marketplace key, account ID or email address. An experience sold on request shows off on both marketplaces, since they confirm bookings straight away.

FieldTypeDescription
channelstringgetyourguide, viator or manawa
namestringThe marketplace's name, as the shop sees it
statestringlive (booked through this marketplace), ready (nothing stops a sale, not booked yet), needs_price (sells, but a price the marketplace uses is missing), fix (cannot sell until the shop acts), switched_off (the marketplace switched it off), off (the shop keeps it off, or has not listed it on Manawa yet)
statusstringThe state in the shop's words, for showing a person
product_idstring|nullThe ID the marketplace uses for this experience. Null on Manawa until the shop lists it there
taking_bookingsbooleanWhether the shop is taking bookings from this marketplace
last_booking_atstring|nullWhen the latest booking from this marketplace was made
problemstring|nullWhat stops a sale, in plain words, or null

Custom Fieldsโ€‹

FieldTypeDescription
field_idintegerCustom field definition ID
labelstringField display label
field_typestringField type: text, textarea, number, date, datetime
valuestring|nullRaw value
formatted_valuestring|nullFormatted value for display
tip

Custom field definitions can be retrieved via GET /v1/settings/custom-fields.

Content Blocksโ€‹

The content_blocks object contains the eight supplier-authored sections rendered on the booking widget (what's included, itinerary, highlights, check-in details, what to bring, restrictions, health & safety, cancellation notes), plus custom_sections โ€” an ordered list of free-form { heading, body } blocks, plus location (where the item happens, used for the map under the check-in / pickup section). The four prose fields and each custom-section body are allowlisted rich-text HTML. The shape is shared with products โ€” see Content Blocks for the full field reference.

Guide requirementsโ€‹

required_certifications and required_languages are what a guide should hold and speak to run the experience. required_certification_names and required_language_names carry the same entries in the words the shop reads on screen, so a list is safe to show a person as it comes back.

They are warn-only: a guide who is missing one is flagged when they are assigned to a departure, and nothing is blocked. Set them with Create Experience or Update Experience.

Errorsโ€‹

StatusCodeDescription
401authentication_requiredNo API key provided
401invalid_api_keyInvalid API key
403insufficient_scopeAPI key lacks experiences:read scope
403forbiddenExperience belongs to different supplier
404resource_not_foundExperience does not exist

Example Requestโ€‹

curl -X GET "https://app.equipdash.com/api/v1/experiences/1" \
-H "Authorization: Bearer eqd_live_abc123xyz789..."

With translated content (the supplier must have that booking widget language enabled):

curl -X GET "https://app.equipdash.com/api/v1/experiences/1?lang=fr" \
-H "Authorization: Bearer eqd_live_abc123xyz789..."

Availability for each booking optionโ€‹

Each row's top-level available_slots is what can still be sold, the same number the booking page shows. When that is below the seats left because gear every guest takes is out on rentals or other departures (or shared places are partly booked), fewer_spots_reason says so in one sentence, for example "5 Single Kayaks are out on a rental then."; otherwise it is null. It never includes a booking number.

Each row in availability includes an option_availability object keyed by booking option ID. Use the chosen option's entry when displaying times or setting quantity limits; the row's top-level counts describe the experience's default behavior.

Field inside each option entryMeaning
availableWhether the option can be booked on this session
available_slotsRemaining booking units; see capacity_unit
available_groupsRemaining group units, or null when not applicable or unlimited
capacity_unitgroups or participants
effective_groups_capacityThe session or option's group ceiling, or null
max_participantsThe pricing block's participant limit, or null for unlimited
available_participantsRemaining participant capacity, or null for unlimited
guide_availableWhether required guide coverage is available

A session reserved for one option reports available: false for the others. The map includes offered options; when the chooser is off it contains only the default. Counts include committed reservations and use the same availability rules as the widget. Adding to a cart does not reserve capacity; booking creation checks it again.