More products
Activities
Activities are searched by destination and date. Each result is one way to do the activity — a language, a ticket type, a time slot — priced for the whole party.
Find a destination
Activity searches need a destination code. Look it up by name:
curl "https://api.roaveagents.com/api/v1/destinations?productType=activity&q=barcel" \
-H "X-API-Key: $ROAVE_API_KEY"{
"data": {
"items": [
{ "_id": "66e0f1a2b3c4d5e6f7a8b9c0", "kind": "activity_destination", "code": "BCN",
"name": "Barcelona", "cityName": "Barcelona", "countryCode": "ES" }
],
"nextCursor": null
},
"meta": {},
"error": null
}Destination codes differ between inventory sources, so one name can return several rows. Send the code from the row you picked exactly as returned; don't build codes yourself.
To show what's available before a traveller picks dates, browse a destination's catalogue with GET /destinations/:code/activities. It lists activity names and images, without prices.
Search
{
"productType": "activity",
"destination": "Barcelona",
"destinationCode": "BCN",
"checkIn": "2026-11-04",
"occupancy": [{ "adults": 2, "childrenAges": [10] }],
"currency": "EUR",
"market": "GB"
}destination(the name) is required, anddestinationCodemakes it precise. Without a code, an unrecognised name returns400 unknown_destination.checkInis the activity date.- Put everyone in one
occupancyentry, with every child's age — prices are often per age band.
Activity searches finish before POST /search returns.
Results
Each row is one option — a modality and, where relevant, a session time — for one activity. Read with groupByProduct=true to page by activity (group the rows by canonicalProductId for your listing), then POST /search/:id/products/:productId/rates to list every option for the activity the traveller opens.
{
"canonicalProductId": "66e0f1a2b3c4d5e6f7a8c111",
"rateQuoteId": "66f2b3d4e4b0c1d2e3f4d200",
"sellAmountMinor": 14800,
"sellCurrency": "EUR",
"refundable": true,
"summary": {
"activityName": "Sagrada Família skip-the-line tour",
"modalityName": "Guided tour in English",
"session": "10:00",
"durationText": "2 hours",
"operationDate": "2026-11-04",
"cancellationPolicy": {
"refundable": true,
"deadlines": [{ "before": "2026-11-03T10:00:00Z", "penaltyMinor": 14800, "currency": "EUR" }]
}
}
}Not every activity has a modality, session or operation date; those fields are null when the inventory doesn't provide them.
Use GET /quotes/:id/details for the full description and images, plus highlights, important information and how to redeem the ticket where the operator provides them. Show the important information and redemption instructions before the traveller books.
Choose an option
Some activities come back as a single row with a “from” price and no time slot. The real options — each time slot, language or ticket type — are only known once you ask. For those, list them before you price-check:
curl -X POST "https://api.roaveagents.com/api/v1/quotes/66f2b3d4e4b0c1d2e3f4d200/activity-recheck" \
-H "X-API-Key: $ROAVE_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'{
"data": {
"quoteId": "66f2b3d4e4b0c1d2e3f4d200",
"bookableOptions": [
{
"supplierRateRef": "rr_q3Jd8Lr2mT0vX9aQe7Kc1bYw",
"reference": "OPT-1",
"title": "Guided tour in English",
"timeSlot": "10:00",
"language": "en",
"price": { "amountMinor": 14800, "currency": "EUR" },
"cancellationPolicy": { "refundable": true, "deadlines": [] },
"panRequired": false,
"bookingQuestions": []
}
]
},
"meta": {},
"error": null
}priceis what you'll pay for that option, worked out the same way the price-check prices it. It can be missing when an option can't be priced yet; show “priced on selection” and let the price-check give the figure.supplierRateRefis an opaque token. Send it back unchanged; don't store it beyond the quote's 20-minute life.- If the party changed since the search, send it as
occupancyin the body to list options for the new party. - Activities that don't have separate options return
400 activity_recheck_unsupported— price-check the row directly.
Then price-check the option the traveller chose by sending its token:
{ "supplierRateRef": "rr_q3Jd8Lr2mT0vX9aQe7Kc1bYw" }From then on the quote books that option. A token that has been altered is refused with 400 validation_error, and a token from a different quote with 400 rate_option_not_offered. When an option has panRequired: true, send panNumber when you book.
Price-check
Price-check the row — or, after choosing an option, send its token as supplierRateRef. If the party changed since the search, send the new party as occupancy in the price-check body to reprice it; if the option can't take that party you get 409 pax_mix_unavailable.
Some activities ask questions the operator needs answered — a pickup hotel, dietary needs. When they do, the price-check's quote.details includes bookingQuestions. Answer them in bookingAnswers when you book, keyed by question code, or by CODE:n for per-traveller questions (n counts travellers from 1):
"bookingAnswers": {
"PICKUP_HOTEL": "Hotel Example, Passeig de Gràcia 1",
"DIETARY:1": "Vegetarian",
"DIETARY:2": "None"
}A missing mandatory answer returns 400 booking_answer_required.
Book
- Send one traveller per participant. Every traveller needs an
age, adults included — operators price and admit by age. Adults need a whole-number age from 18 to 120; otherwise the booking returns400 traveler_manifest_mismatch. - The lead traveller needs
emailandphone; the operator uses them on the day. - Always send a
titleon the lead traveller (Mr,Mrs,Ms,Miss,MasterorDr). Some activities need it, the quote doesn't say which, and without it they return400 traveler_title_required. - Pay with
paymentMethod: "credit". Activities are sold net.
Activities usually confirm straight away. If one returns supplier_pending, the operator is still confirming — see Booking statuses.
The operator's voucher
When the operator issues one, a confirmed activity carries the operator's own voucher in the line item's details.supplierVouchers. That voucher is the ticket. Give it to the traveller unchanged; a voucher you design yourself isn't accepted for entry on its own.
Cancel
Cancel with POST /bookings/:id/cancel. Many activities are free to cancel until a day or two before; check cancellationPolicy first, as described in Cancellations and refunds.