Overview
An API key lets a system you control talk to Bella on your business’s behalf. The things people build most often are:- A booking form on your own website - read your services, check availability, then create the client and the appointment.
- A sync into another system - pull your clients and appointment history into a CRM, a data warehouse or a backup.
403.
A key belongs to one location. If you run more than one location, switch to that location before creating its key. A key can never be moved to a different location.
Quickstart
Four calls take a visitor from “what do you offer?” to a booked appointment.Authentication
Send the key in thex-api-key header on every request. You never need to say which location - the key already knows.
Managing keys
1
Open Settings → Integrations
You’ll need permission to manage settings. You can only grant a key permissions you hold yourself. A key with Read appointments sees every appointment, so granting it also needs permission to view all team members’ appointments.
2
Name it after what will use it
“Website booking”, “CRM sync”, “Ops bot” - so the Last used column tells you something useful later.
3
Choose what it can do
Tick only what that integration actually needs. You can change this later without reissuing the key.
4
Copy the key
It’s shown once. Store it as an environment variable on your server. If you lose it, revoke it and create another.
- Edit - rename it or change its permissions. The key itself never changes, so whoever is using it doesn’t have to do anything.
- Revoke - stop it working, keep the row so you can still see when it was last used. This cannot be undone.
- Delete - remove it from the list entirely. If the key was still active this also stops it working.
Edits, revocations and deletions all take effect within a minute rather than instantly - keys are cached briefly to keep requests fast.
Permissions
Everything else returns
403. A key cannot reach sales, reports, payments, team management, settings, or the API keys themselves - no key can ever create another key. There’s no call for sending a text or email either: clients get only your usual automated messages, such as the confirmation for a new appointment.
API reference
Base URL:https://api.bellabooking.com
Requests and responses are JSON. All ids are strings. Dates are YYYY-MM-DD; times of day are HH:MM:SS.
List the catalogue
Check availability
variantId where the service uses variants, and addonDurationInMinutes (the extra minutes of the add-ons the client chose) where it has add-ons, so the slots you get back match the real length of the appointment.
Availability reflects rosters, existing appointments and blocked time. It applies your minimum-notice period as your booking page does, but not your gap-reduction settings - see Booking rules.
Create or match a client
400. A key that can also Read client records gets that client named in detail (“Jane Citizen already has this phone number.”) and their id and name in existingClient; any other key gets “A client with this phone number already exists.” Look a returning client up first with GET /api/Clients/search?filter= and their phone or email, and create one only when nothing comes back. A new client with exactly the same name as an existing one is refused too, unless you send "forceCreateDespiteDuplicateName": true (two people can share a name).
notes (a private note only your team sees) and tags (client tags, e.g. ["Booked by phone"]) are optional.
Create an appointment
Pricing your booking correctly
Bella records theprice and durationInMinutes you send. It does not re-read them from your catalogue. That means:
- Always read them from
GET /api/servicecategoriesat booking time. Hard-coded prices go stale the moment you change a price in Bella. - Where a service uses variants, take the price and duration from the chosen variant, not the base service.
- Where a service uses pricing tiers, take them from the tier matching the assigned team member.
- Add-ons: keep their price out of
price, because Bella adds each add-on’s catalogue price to the line itself. Put their minutes in:durationInMinutesis the line’s full length, add-ons included, and is recorded as sent. - The one exception is a bundle with a set total (Set total price, % discount or Free): Bella replaces the
priceon each of its lines with that line’s share of the total, whatever you send.
Booking rules
Bookings made with an API key are recorded with the same checks your front desk gets, not the stricter ones your public booking page gets. Bella will accept a booking that:- double-books a team member
- is in the past, or inside your minimum-notice period
- is with a team member who isn’t qualified for that service
- is a members-only service for someone without a membership
- has unmet prerequisite services (a treatment that online booking would lock until a consult is booked or completed)
- would have needed your approval if it came through your own booking page
Services that need a room or piece of equipment take a
resourceId on the line. Your own booking page picks a free resource automatically; the API doesn’t, so a line without one is refused with a message naming the service that needs one. Check availability for that resource first.Reschedule an appointment
GET /api/Appointments/availability and rescheduleAppointmentId, and move only to a time that came back. A time in the past, or a cancelled or completed appointment, is refused with 400.
To change the team member, services or prices as well, use Edit an appointment.
Edit an appointment
GET /api/Appointments/{id}, change what you need, and send the whole appointment back. What you send replaces what’s there.
Each line takes the fields the read returns for it:
serviceId, teamMemberId, startTime, durationInMinutes, price, variantId, bundleId, resourceId, addons and discount. Change only what you need. A line’s discount left out is removed, and a line whose service needs a room or piece of equipment is refused without its resourceId. A line’s add-ons, guests and group are kept when left out, and so are the appointment’s products, custom items and tags.
silentNotification.
- Check availability first. Like a front-desk edit, it accepts a team member who is already booked. A time outside a team member’s working hours is refused only when Enforce working hours is on in Settings → Scheduling.
- Read just before you write. There’s no conflict check: if your team changes the appointment between your read and your edit, your edit replaces their change.
- Prices are recorded as sent, the same as when booking.
- A cancelled or checked-out appointment can’t be edited (
400).
Cancel an appointment
Always send a JSON body, even if it’s just
{}.
refundAmount is null when the appointment held no paid deposit, and 0 when it held one and nothing was sent back (the policy kept it, or it was paid in person and you hand it back yourself). Each cancel adds one to the client’s cancellation count, or to their no-show count with reasonId 6.
Packages, memberships, gift-card balances, loyalty points and promo codes the appointment used are restored automatically.
Cancelling through the API never charges a client’s card on file. A deposit paid online by card is refunded automatically, less what your late cancellation or no-show fee takes on a late cancellation (all of it when your deposit rule keeps the whole deposit). To charge the rest of a fee to a card on file, cancel from the dashboard, or use Charge fee on the cancelled appointment.
Check a client in
checkInState is NotArrived, Waiting or InProgress. Times are UTC.
Check-in is separate from the appointment’s status. A checked-in appointment is still Booked or Confirmed; only checkout completes it. So these two calls cannot confirm, complete or cancel anything, and an appointment nobody checked in still checks out normally - “not arrived” is not a no-show.
You can call start without calling checkin first, for a front desk that only marks the service starting. Bella backfills the arrival to the same instant, so the wait time reads zero. Both calls are safe to repeat: a second one changes nothing.
Checking in notifies the assigned team member in the dashboard, the same as a front-desk check-in. The client is never messaged - they are standing in front of you.
Check-in clears if the appointment later moves to a different day. A same-day time change keeps it. Both calls return
400 on a cancelled or completed appointment. Undoing a check-in is dashboard-only for now.Read clients
search accepts filter (name, email or phone), limit, skip and a range of filters including createdAfter, createdBefore, lastAppointmentAfter and tags.
Read appointments
search requires startDate and endDate, and accepts teamMemberIds, serviceIds, statusIds (1 Booked, 2 Confirmed, 3 Completed, 4 Cancelled), clientFilter (a client’s name, email or phone, in any format, or an appointment tag), limit (up to 200), skip, orderBy and orderByDirection. Times come back in your business’s local time. The search lines carry no variantId; GET /api/Appointments/{id} has it.
There’s no incremental cursor yet. A sync re-reads the date or page range it asks for. Deleted records leave no tombstone, so a full reconciliation is more reliable than a running total.
Errors
Errors are returned as JSON with an HTTP status and adetail explaining what went wrong.
Rate limits
Limits are applied per key, per minute.
Exceeding a limit returns
429 with a Retry-After header. Wait that many seconds and retry - don’t retry immediately in a loop.
The write limit is sized for a real booking form rather than a bulk import - so if you’re migrating data or running a busier site, ask us to raise it rather than working around it. These limits protect your business’s own performance and may change; they aren’t a service guarantee.
Keeping your key safe
- Store it as an environment variable or in a secrets manager, never in your source code.
- Give each integration its own key, so you can revoke one without breaking the others.
- Revoke a key the moment it’s no longer needed, or the moment you suspect it’s been exposed.
- Narrow a key’s permissions when an integration stops needing something - editing is safer than leaving a key more capable than it has to be.
- Your key can read and change real client data. Treat it like the password to your dashboard.
Finding this API
Agents and crawlers discover the website-booking API from the Bella marketing site - not from an OpenAPI file on this docs host:- How to authenticate: https://bellabooking.com/.well-known/auth.md
- API catalog: https://bellabooking.com/.well-known/api-catalog
- Public docs MCP (documentation search only - not this API): https://bellabooking.com/.well-known/mcp/server-card.json