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โ
| Parameter | Type | Location | Description |
|---|---|---|---|
id | integer | path | The experience ID |
lang | string | query | Return 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.
| Field | Type | Description |
|---|---|---|
booking_options | array | Booking 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
archivedbefore offering a choice. - The option's own values win over the
pricing.structureandis_privatefields, which describe the experience as a whole. Readprice_structureon the option for how it is priced, andexclusivefor 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.
| Field | Type | Description |
|---|---|---|
channel | string | getyourguide, viator or manawa |
name | string | The marketplace's name, as the shop sees it |
state | string | live (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) |
status | string | The state in the shop's words, for showing a person |
product_id | string|null | The ID the marketplace uses for this experience. Null on Manawa until the shop lists it there |
taking_bookings | boolean | Whether the shop is taking bookings from this marketplace |
last_booking_at | string|null | When the latest booking from this marketplace was made |
problem | string|null | What stops a sale, in plain words, or null |
Custom Fieldsโ
| Field | Type | Description |
|---|---|---|
field_id | integer | Custom field definition ID |
label | string | Field display label |
field_type | string | Field type: text, textarea, number, date, datetime |
value | string|null | Raw value |
formatted_value | string|null | Formatted value for display |
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โ
| Status | Code | Description |
|---|---|---|
| 401 | authentication_required | No API key provided |
| 401 | invalid_api_key | Invalid API key |
| 403 | insufficient_scope | API key lacks experiences:read scope |
| 403 | forbidden | Experience belongs to different supplier |
| 404 | resource_not_found | Experience 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 entry | Meaning |
|---|---|
available | Whether the option can be booked on this session |
available_slots | Remaining booking units; see capacity_unit |
available_groups | Remaining group units, or null when not applicable or unlimited |
capacity_unit | groups or participants |
effective_groups_capacity | The session or option's group ceiling, or null |
max_participants | The pricing block's participant limit, or null for unlimited |
available_participants | Remaining participant capacity, or null for unlimited |
guide_available | Whether 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.