For companies with their own booking system
Connect your booking system to Spot
Keep your system as the source of truth. Spot shows your live availability to travellers, and either sends them to your site or takes the booking and passes it straight to you.
Two ways to connect
Option 1
Show your live times, book on your site
Spot reads your availability every 15 minutes and lists your experiences with their next open times. The Book button opens your own booking page, tagged utm_source=spottours, so it shows in your analytics.
- ✓No commission on these bookings
- ✓Your system stays the only place customers pay
- ✓Publish one JSON file and Spot does the rest
Option 2
Customers book and pay on Spot
Customers pick a time, choose options and extras, and pay on Spot. Spot re-checks the time with your system just before charging, then sends the booking straight into your system.
- ✓You get 87.5%, paid out after the experience runs
- ✓Spot keeps 12.5%, card fees included
- ✓Spot emails the customer their confirmation and handles refunds
Start with option 1 and switch to option 2 when you're ready. Either way you get your own company page on Spot, and your experiences show on the map, in Explore and in the AI trip planner. Plans and pricing are on listing your company.
What Spot needs from you
Your experiences
Name, description, photos, duration, prices (adult/child), group size, and any options or extras customers choose.
Your open times
Which dates and start times are bookable, plus closures, holidays and anything already booked. If one booking blocks other experiences (one kitchen, one boat), tell us.
One feed URL
Publish your experiences and open times as a Spot feed: one JSON file at a URL (spec below), which you can check yourself. No developer? Send us your booking system's API details and we'll connect it for you.
How it goes
- 1
Send us your feed URL
Check it with the checker below, then send it to us. Your experiences appear on Spot, refreshed every 15 minutes, and are hidden automatically if your feed is unreachable.
- 2
You claim your page
We send you a one-time code. Claim your company page to manage it, see every Spot booking, and connect your payout account.
- 3
Optional: take bookings
Your developer adds one endpoint (spec below). Spot bookings then land in your system automatically, labelled as Spot.
Got a code already? Claim your company page →
For developers
Publishing a Spot feed
One JSON document at a public https URL, listing your experiences and every open start time. Build it from your own system on each request (or regenerate it every few minutes). Spot reads it every 15 minutes, and with Spot checkout on, again right before charging, so keep it current. See a live example.
{
"spotFeedVersion": 1,
"currency": "eur",
"experiences": [
{
"id": "sunset-sail",
"title": "Sunset Sail",
"summary": "Two hours on a classic sailing boat at sunset.",
"description": "Longer text, plain paragraphs…",
"theme": "sailing",
"durationMinutes": 120,
"photos": ["https://your-site.com/img/sail-1.jpg"],
"highlights": ["Local skipper", "A glass of cava"],
"price": { "adultCents": 4500, "childCents": 2500 },
"booking": "per-person",
"maxGroup": 12,
"meetingPoint": { "address": "Port Vell", "latitude": 41.3765, "longitude": 2.1823 },
"bookingUrl": "https://your-site.com/book/sunset-sail",
"extras": [{ "id": "cava", "name": "Bottle of cava", "priceCents": 2000 }],
"choices": [{ "key": "menu", "label": "Menu", "options": ["Fish", "Vegetarian"] }],
"slots": [
{ "date": "2026-10-30", "time": "18:30", "spotsLeft": 8 },
{ "date": "2026-10-31", "time": "18:30", "spotsLeft": 12 }
],
"active": true
}
]
}Fields
- spotFeedVersion
- Always 1.
- currency
- Lowercase currency code, and it must be your city's currency on Spot (eur for Barcelona, gbp for London…). All prices are in cents of it.
- id
- Your stable id for the experience: lowercase letters, digits, - and _. Never reuse one for a different experience; we use it in links and bookings.
- title, summary, description
- Title is required. Summary is a line or two; description can be longer.
- theme
- Optional, One of: food, wine-farm, sailing, cycling, nightlife, gaudi, gothic, old-city, jewish-quarter. Anything else shows under Other.
- photos
- https image URLs, the first is the cover. Up to 8.
- price.adultCents, price.childCents
- Price per adult and per child, in cents. 0 means free.
- booking
- "per-person" (people buy places on a shared departure) or "private" (one booking takes the whole time; priced per adult with an optional minAdults).
- minAdults, maxGroup
- Smallest number of adults charged (private), and the largest group.
- resource
- Private only. Experiences that share something (one kitchen, one boat) use the same resource name, so a Spot booking on one closes that time on the others until your feed catches up.
- meetingPoint
- Address, plus latitude and longitude for the map pin.
- bookingUrl
- Where Spot's Book button sends people when they book on your site. Spot adds utm_source=spottours.
- extras, choices
- Optional paid add-ons, and required questions (e.g. which menu). Their ids come back in bookings.
- slots
- Every bookable start: date (YYYY-MM-DD) and time (HH:MM), local time. List only times you would really sell, after your own closures, minimum notice and existing bookings. spotsLeft (per-person) hides full times. Up to 120 days ahead.
- active
- false hides the experience on Spot without removing it.
Check your feed
Shows exactly what Spot would list from your URL: each experience, its open times, and anything Spot would skip and why.
For developers
Receiving Spot bookings
For option 2. Add one HTTPS endpoint to your system and send us its URL. Spot POSTs a JSON event to it whenever a Spot booking is confirmed or cancelled. This page is the full spec, so you can hand it to your developer or your AI coding agent as it is.
Security
- Spot and you share a secret (at least 32 random bytes, e.g.
openssl rand -hex 32). Keep it in an environment variable such asSPOT_WEBHOOK_SECRET. Exchange it privately, never by email or chat, and never commit it. - Every request carries
X-Spot-Signature: the hex HMAC-SHA256 of the raw request body, keyed with the secret. - Other headers:
Content-Type: application/json,User-Agent: SpotTours-bookings/1.0.
booking.confirmed
Paid and confirmed on Spot. Create it in your system as confirmed and paid, and block the time. Dates and times are local to the experience. choices and extras use the ids we agree when we connect you; amounts are in cents.
{
"type": "booking.confirmed",
"eventId": "8fQ2…_booking.confirmed",
"bookingId": "8fQ2…",
"source": "spottours",
"class": "your-experience-id",
"tourTitle": "Private Paella Class",
"date": "2026-10-30",
"time": "11:00",
"adults": 3,
"children": 0,
"choices": { "paellaType": "Seafood paella" },
"extras": [{ "id": "rioja_red", "name": "Rioja red wine", "qty": 1, "cents": 5500 }],
"notes": "One guest is allergic to shellfish",
"phone": "+44 7700 900000",
"amountPaidCents": 38500,
"currency": "eur"
}booking.cancelled
Cancelled on Spot, and the customer has already been refunded. Mark it cancelled and reopen the time.
{
"type": "booking.cancelled",
"eventId": "8fQ2…_booking.cancelled",
"bookingId": "8fQ2…",
"source": "spottours",
"class": "your-experience-id",
"tourTitle": "Private Paella Class",
"date": "2026-10-30",
"time": "11:00",
"refundedCents": 38500,
"currency": "eur",
"reason": "Customer's flight was cancelled"
}Rules for your endpoint
- Verify the signature over the raw request body, before parsing JSON. A missing or wrong signature → 401, and create nothing.
- Be idempotent: the same event can arrive more than once. Key on bookingId (and store eventIds you've handled), so a repeat never creates a second booking or a second email.
- Mark the booked time as taken in your system, so it can't be sold twice and Spot's next availability read shows it as booked.
- Spot has already charged the customer and emailed their confirmation. Don't charge or email them again.
- A booking.cancelled for a bookingId you don't have: return 2xx and do nothing.
- An unknown type: return 2xx and ignore it, so new event types never break you.
- Reply within 10 seconds, and only return 2xx once the change is saved.
Retries
Anything other than a 2xx is retried after 5 minutes, 15 minutes, 1 hour, 3, 6, 12 and 24 hours: 8 tries over about two days. If every try fails, Spot emails you the booking so you can enter it by hand. Every booking is also emailed to you when it's made, so nothing is lost while your endpoint is down.
Example: verifying a request (Node / Next.js)
import { createHmac, timingSafeEqual } from "node:crypto";
export async function POST(req: Request) {
const secret = process.env.SPOT_WEBHOOK_SECRET;
const raw = await req.text(); // the raw body, before JSON parsing
const expected = createHmac("sha256", secret ?? "").update(raw).digest("hex");
const given = req.headers.get("x-spot-signature") ?? "";
if (!secret || given.length !== expected.length ||
!timingSafeEqual(Buffer.from(given), Buffer.from(expected))) {
return new Response("Bad signature", { status: 401 });
}
const event = JSON.parse(raw);
// …create or cancel the booking, idempotent on event.bookingId…
return Response.json({ ok: true });
}Testing it
Post a signed test event yourself. An unsigned request should get 401, the signed one 2xx, and posting it twice should still give one booking. Then send the matching booking.cancelled and delete the test booking. When it works, send us the endpoint URL and we'll send a real test from Spot.
BODY='{"type":"booking.confirmed","eventId":"test1_booking.confirmed","bookingId":"test1","source":"spottours","class":"your-experience-id","tourTitle":"Test","date":"2026-12-15","time":"11:00","adults":2,"children":0,"choices":{},"extras":[],"notes":"TEST - delete","phone":"","amountPaidCents":0,"currency":"eur"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SPOT_WEBHOOK_SECRET" -hex | sed 's/^.* //')
curl -i -X POST -H "Content-Type: application/json" \
-H "X-Spot-Signature: $SIG" -d "$BODY" https://your-site.com/your-endpoint