Skip to main content

Bookings API: take bookings on your own website

A small, secure JSON API for your campsite: read your pitch types, extras, availability and prices, and create bookings. Everything after that is managed in your Campiamo dashboard.

Last updated November 9, 2026

The Bookings API lets your own website put bookings into Campiamo. It does two things: it tells you what can be booked (pitch types, extras, availability and prices), and it creates bookings. Changing, cancelling, taking balances and checking guests in all happen in your Campiamo dashboard, as they do for every other booking.

The Bookings API is part of the Growth plan and above.

If all you want is a booking form on your own website, you do not need the API: the booking widget does that with one line of code. The API is for when you want to build the booking form yourself.

Before you start

  1. In your dashboard go to Settings → API.
  2. Choose Create key, give it a name you will recognise ("Website", "Booking form") and confirm with your password.
  3. Copy the key. It is shown once. We store only a fingerprint of it, so nobody, including us, can show it to you again. If you lose it, revoke it and create another.

A key belongs to one campsite. You can have up to five, and you can revoke any of them at any time; a revoked key stops working at once.

Keep the key on your server

The key is a password to create bookings at your campsite. Treat it like one:

  • Call the API only from your server, never from JavaScript in a web page or from a mobile app, where anyone can read it. The API refuses calls from browsers on other sites.
  • Do not put it in a web address, an email or a public code repository.
  • If you know the address your server calls from, lock the key to it under Allowed IP addresses.
  • If you think a key has leaked, revoke it. You get an email whenever a key is created or revoked on your account.

Making a request

The base address is:

https://campiamo.com/api/v1

Send your key in the Authorization header on every request, over HTTPS:

curl https://campiamo.com/api/v1/campsite \
  -H "Authorization: Bearer cmp_live_YOUR_KEY" \
  -H "Accept: application/json"

Requests and responses are JSON. Dates are YYYY-MM-DD, and check_out is the morning the guest leaves. Prices are numbers in your campsite's own currency.

What can be booked

Your campsite

GET /campsite returns what a booking form needs to know about your site.

{
  "name": "Meadow Fields Campsite",
  "currency": "GBP",
  "timezone": "Europe/London",
  "language": "en",
  "check_in_time": "14:00",
  "check_out_time": "11:00",
  "open_season": "1 March to 31 October",
  "guest_fields": { "phone_required": false },
  "age_bands": { "enabled": true, "child_min": 2, "child_max": 15, "adult_from": 16, "infants": true, "infant_under": 2, "infants_take_space": false },
  "booking_questions": [],
  "club_memberships": [],
  "discount_codes": false,
  "gift_vouchers": false,
  "payment": {
    "online_payment": true,
    "provider": "Stripe",
    "terms": "A deposit of 25% is paid at checkout; the balance is due 14 days before arrival."
  }
}

booking_questions are your custom booking fields. Send the answers back in answers when you create a booking, keyed by each question's id.

Pitch types

GET /pitch-types lists the pitch types and accommodation guests can book.

{
  "pitch_types": [
    {
      "id": "01kwz3mjx2abcdef0123456789",
      "name": "Grass pitch with electric",
      "type": "pitch",
      "description": "Level grass pitches with a 16A hook-up.",
      "photos": ["https://campiamo.com/img/01kx.../1280"],
      "base_price": 28.0,
      "pricing": "per_night",
      "included_adults": 2,
      "included_children": 0,
      "extra_adult_price": 5.0,
      "extra_child_price": 3.0,
      "max_persons": 6,
      "adults_only": false,
      "dogs_allowed": true,
      "max_dogs": 2,
      "extra_dog_price": 2.0,
      "min_nights": 1,
      "max_nights": null,
      "units": 12
    }
  ]
}

base_price is the starting price. Seasons, minimum stays and party size change what a stay costs, so always take the price for real dates from availability or a quote.

Extras

GET /extras lists the extras a guest can add. pitch_type_ids is empty when an extra is offered with every pitch type.

{
  "extras": [
    {
      "id": "01kx0a...",
      "name": "Firewood bundle",
      "description": "A net of seasoned logs.",
      "price": 8.0,
      "child_price": null,
      "price_type": "fixed",
      "max_quantity": 5,
      "choice_label": null,
      "choices": [],
      "pitch_type_ids": []
    }
  ]
}

Availability

GET /availability answers "what is free for these dates and this party, and what does it cost?"

Parameter
check_in required Arrival date
check_out required Departure date
adults optional Default 2
children optional Default 0
infants optional Default 0
dogs optional Default 0
curl "https://campiamo.com/api/v1/availability?check_in=2027-06-12&check_out=2027-06-15&adults=2&children=1" \
  -H "Authorization: Bearer cmp_live_YOUR_KEY"
{
  "check_in": "2027-06-12",
  "check_out": "2027-06-15",
  "nights": 3,
  "party": { "adults": 2, "children": 1, "infants": 0, "dogs": 0 },
  "currency": "GBP",
  "available": [
    {
      "pitch_type_id": "01kwz3mjx2abcdef0123456789",
      "name": "Grass pitch with electric",
      "units_free": 4,
      "max_persons": 6,
      "dogs_allowed": true,
      "total_price": 93.0,
      "due_now": 23.25
    }
  ],
  "unavailable": [
    { "pitch_type_id": "01kwz3mjx9...", "name": "Bell tent", "reason": "minimum_stay", "detail": "minimum stay 4 nights for those dates" }
  ]
}

due_now is what the guest would pay at checkout under your deposit rules. The reason for an unavailable pitch type is one of closed, adults_only, too_many_people, no_dogs, too_many_dogs, maximum_stay, not_offered, minimum_stay, fully_booked or no_price.

A quote

POST /quotes prices one pitch type with extras and codes, without booking anything. It takes the same stay fields as a booking.

curl https://campiamo.com/api/v1/quotes \
  -H "Authorization: Bearer cmp_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "pitch_type_id": "01kwz3mjx2abcdef0123456789",
    "check_in": "2027-06-12",
    "check_out": "2027-06-15",
    "adults": 2,
    "children": 1,
    "extras": [{ "extra_id": "01kx0a...", "quantity": 2 }]
  }'
{
  "pitch_type_id": "01kwz3mjx2abcdef0123456789",
  "name": "Grass pitch with electric",
  "check_in": "2027-06-12",
  "check_out": "2027-06-15",
  "nights": 3,
  "party": { "adults": 2, "children": 1, "infants": 0, "dogs": 0 },
  "currency": "GBP",
  "accommodation_total": 84.0,
  "guest_extras": [{ "name": "Extra child", "total": 9.0 }],
  "extras": [{ "extra_id": "01kx0a...", "name": "Firewood bundle", "quantity": 2, "choice": null, "total": 16.0 }],
  "club_membership": null,
  "tourist_tax": null,
  "discount": null,
  "voucher": null,
  "total": 109.0,
  "due_now": 27.25
}

guest_extras are the charges for people or dogs beyond those included in the pitch price. tourist_tax is filled in when your campsite collects a tourist tax; when it is collected with the booking it is already inside total.

Creating a booking

POST /bookings creates a booking. It is checked by the same rules as your booking widget: the season, availability, minimum stays, party size, dogs, required questions and extras. If a guest could not make the booking on your booking page, the API will not make it either.

Field
pitch_type_id required From pitch types or availability
check_in, check_out required Today or later
adults required At least 1
children, infants, dogs optional Default 0
child_ages required with children One age per child, e.g. [4, 9]
guest.name, guest.email required
guest.phone optional Required if your campsite requires a phone number
guest.address optional line_1, line_2, city, county, postcode
extras optional [{ "extra_id": "...", "quantity": 1, "choice": "..." }]
answers optional Answers to your booking questions, keyed by question id
discount_code, voucher_code optional
notes optional Up to 1,000 characters
payment required checkout or on_arrival, see below
send_guest_emails optional on_arrival only. Default true
price optional on_arrival only. Your own total for this booking, in place of the calculated one

Guest pays online: "payment": "checkout"

Use this when a guest is booking on your website. You get back a link to a secure payment page hosted by your payment provider. Send the guest's browser to it.

curl https://campiamo.com/api/v1/bookings \
  -H "Authorization: Bearer cmp_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7f1c2c0e-2f0b-4c59-9a3b-6f0d1f5b8a11" \
  -d '{
    "pitch_type_id": "01kwz3mjx2abcdef0123456789",
    "check_in": "2027-06-12",
    "check_out": "2027-06-15",
    "adults": 2,
    "children": 1,
    "child_ages": [9],
    "guest": { "name": "Sam Carter", "email": "sam@example.com", "phone": "07700 900123" },
    "payment": "checkout"
  }'
{
  "status": "payment_required",
  "checkout_url": "https://checkout.stripe.com/c/pay/cs_live_...",
  "expires_at": "2026-11-10T10:42:00+00:00",
  "pitch_type_id": "01kwz3mjx2abcdef0123456789",
  "check_in": "2027-06-12",
  "check_out": "2027-06-15",
  "nights": 3,
  "currency": "GBP",
  "total": 93.0,
  "due_now": 23.25
}

The booking is made when the guest pays. The pitch is held while they are on the payment page. Once they pay, the booking appears in your dashboard, they land on a confirmation page and they receive your confirmation email with their booking reference. If they leave without paying, nothing is booked and the pitch is released.

Your campsite must take online payments for this (connect Stripe or PayPal). If nothing is due at checkout, for example a gift voucher covers it, the booking is confirmed at once and the response is the confirmed one below with a confirmation_url for the guest.

No online payment: "payment": "on_arrival"

Use this for a booking that needs no payment page: a guest who will pay you on arrival, or a booking you took by phone. The booking is confirmed straight away.

curl https://campiamo.com/api/v1/bookings \
  -H "Authorization: Bearer cmp_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: phone-2027-0142" \
  -d '{
    "pitch_type_id": "01kwz3mjx2abcdef0123456789",
    "check_in": "2027-06-12",
    "check_out": "2027-06-15",
    "adults": 2,
    "guest": { "name": "Sam Carter", "email": "sam@example.com" },
    "payment": "on_arrival",
    "send_guest_emails": false,
    "price": 80
  }'
{
  "status": "confirmed",
  "booking": {
    "reference": "BK-7QK2MD",
    "pitch_type_id": "01kwz3mjx2abcdef0123456789",
    "pitch_type": "Grass pitch with electric",
    "check_in": "2027-06-12",
    "check_out": "2027-06-15",
    "nights": 3,
    "adults": 2,
    "children": 0,
    "infants": 0,
    "dogs": 0,
    "currency": "GBP",
    "total": 80.0,
    "payment_status": "pay_on_arrival"
  },
  "dashboard_url": "https://campiamo.com/dashboard/bookings/01m7..."
}
  • With send_guest_emails left on, the guest gets your usual confirmation email. Turn it off when the guest already knows about the booking. You still get your own new-booking alert either way.
  • price sets your own price for the stay, the same as the custom price box on a dashboard booking. A tourist tax collected with the booking is added on top of it. Leave price out and Campiamo calculates the price.
  • Record what the guest has paid from the booking's page in your dashboard.

Send each booking once

Networks fail. If you do not get an answer, you cannot tell whether the booking was made. Send an Idempotency-Key header with every POST /bookings: any unique text of your own, such as your order number. If you repeat a request with the same key and the same contents, you get the original answer back and no second booking is made.

When something is wrong

Errors come back as JSON with a matching HTTP status. code is stable and safe to test for; message is for people.

{
  "error": { "code": "validation_failed", "message": "The booking could not be made." },
  "errors": { "check_out": ["This accommodation requires a minimum stay of 2 nights for the selected dates."] }
}
Status code What it means
401 invalid_key The key is missing, wrong or revoked
403 browser_request The call came from a web page. Call the API from your server
403 key_in_url The key was sent in the web address. Send it in the header and revoke that key
403 ip_not_allowed The call came from an address the key is not locked to
403 plan_required Your plan no longer includes the API
403 api_disabled The API is switched off for your account. Contact support
404 not_found No such address, or the id is not one of yours
409 request_in_progress A request with this Idempotency-Key is still running
422 validation_failed A field is missing or not allowed. errors says which
422 not_available The pitch type cannot be booked for those dates
422 online_payment_unavailable checkout was asked for but the campsite takes no online payments
422 booking_refused The booking cannot be taken online. Handle it in your dashboard
422 idempotency_key_reused The Idempotency-Key was already used for a different request
429 rate_limited Too many requests. Retry-After says how long to wait
503 bookings_paused Bookings are briefly paused for maintenance. Try again shortly
503 api_disabled The API is switched off for everyone for a short time. Try again later

Limits

  • 120 requests a minute per key for looking things up.
  • 30 bookings an hour and 200 a day per key.

If a key reaches the daily booking limit we email you, because that usually means a script has gone wrong or a key has leaked. If you have a real need for more, tell us.

What the API does not do

By design, the API cannot read, change or cancel existing bookings, read your guest list, record payments or see traveller registration data. That keeps your guests' information out of reach of a leaked key, and it keeps one place, your dashboard, as the record of every booking.

Still stuck?

If this page didn't answer your question, it's our page that needs fixing. Tell us.

Get in touch