// SIMPLYBOOK API

SimplyBook callback URL: the setup nobody tells you about

It is empty by default, it is unsigned, it warns you about nothing, and hosting bot-protection can eat it silently. What SimplyBook actually posts, how to secure it, and how to make its silence visible.

  • ● Unsigned by default
  • ● Silent failures
  • ● From a live build

Where the callback URL actually lives

In SimplyBook’s admin, go to Settings → API. On that page, alongside your API key, there is a single box labelled Callback URL. That one field is the whole webhook configuration. There is no event picker, no secret field, no signing key, no delivery log to inspect afterwards. You paste a URL, save, and SimplyBook will POST to it when bookings change.

By default that box is empty. This is the single most expensive fact in this guide, so it is worth stating plainly: if you never fill it in, nothing reaches your server, and nothing anywhere tells you so. Your integration will happily create bookings through the API and look perfectly healthy, while every booking taken inside SimplyBook itself — by a member of staff, by a customer using a SimplyBook page, by an admin moving an appointment — is invisible to you. There is no warning banner, no red dot, no “you have no callback configured” nudge. Silence looks exactly like success.

What SimplyBook actually POSTs

The request is a raw JSON body (not form-encoded), and it is deliberately thin. You get:

  • booking_id — the booking’s numeric id
  • booking_hash — a per-booking hash you need in order to read it back
  • company — your company login
  • notification_type — one of create, cancel, notify or change

That is it. No customer name, no service, no start time, no price. The callback is a doorbell, not a delivery: it tells you something happened to a booking and leaves you to go and fetch the detail yourself. Design for that from the start — treat the payload as an identifier plus a hint, and never try to render a confirmation from the webhook body alone.

Because the body is JSON, read the raw input stream rather than the usual form-parsed superglobals. Plenty of half-finished integrations fail here first: the endpoint returns 200, the framework sees an empty request, and the booking quietly evaporates.

It is unsigned — you must authenticate it yourself

There is no HMAC header, no shared signing secret, no signature to verify. Anyone who learns your callback URL can POST whatever they like to it. Since the URL is the only thing SimplyBook lets you configure, the URL has to carry the secret:

https://example.co.uk/api/booking-callback.php?t=<long-random-token>

Compare that token in constant time — hash_equals() in PHP — rather than with ==, so the comparison cannot be probed a character at a time. Then check the company field in the payload against your own company login and reject anything that does not match. Both checks are cheap, and together they mean a stranger needs your exact token and your company name before they can even make your code do work.

A few habits that go with an unsigned webhook: generate the token from a cryptographically secure source and keep it out of your public repository, keep it out of client-side code entirely, and rotate it by pasting a new URL into the Settings → API box if you ever suspect it has leaked. Log rejected requests, but log the fact of the rejection, not the token that was offered.

Reading the booking back

Once the request is authenticated, fetch the real detail with the admin API method getBookingDetails(id, sign). The signature is a plain MD5 of three values concatenated in order — the booking id, the booking hash from the callback, and your API secret key:

sign = md5(bookingId . bookingHash . secretKey)

The hash arrives with the callback precisely so you can prove you received a genuine notification for that booking. Do not cache it, do not put it in a URL you hand to a browser, and do not log it next to the booking id.

One rule that saves a lot of grief: trust the booking’s own status, not the order the webhooks arrived in. Duplicate and late deliveries happen. You will see a create land after a cancel for the same appointment, or the same create twice. If you build state from the event stream you will resurrect cancelled appointments and email customers about slots that no longer exist. Build state from the booking record you just fetched — if it reports itself cancelled, it is cancelled, whatever the notification said. Make the handler idempotent, keyed on the booking id, so a repeat delivery is a no-op rather than a second confirmation email.

Return the right status code, or lose the booking

SimplyBook retries on a non-2xx response. That is your only safety net, and it is easy to throw away.

The failure mode looks like this: your handler receives the callback, tries getBookingDetails, the API call times out or the token refresh fails, and your code — being polite — catches the error and returns 200 so the caller is not left hanging. SimplyBook now believes the notification was delivered successfully and never sends it again. That booking is gone from your system permanently, and the customer’s confirmation email is never sent.

So: return 5xx on any transient failure — the lookup failed, the queue file could not be written, the database was unreachable. Return 200 only when you have genuinely finished with the message, which usually means “safely persisted, will process shortly”. Accept-then-process is the right shape here: write the job somewhere durable, respond 200, do the slow work afterwards. Reserve a 200 for authentication failures too, oddly enough — there is no point asking SimplyBook to retry a request you will always reject.

Configured is not the same as working

Filling the box in does not prove the POST arrives. Shared hosting bot-protection sits in front of your site and does not know the difference between a scraper and a legitimate server-to-server POST from an API vendor. We have seen a hosting platform’s protection intercept exactly this kind of request, so the URL was correct, the code was correct, and nothing ever ran. Nothing in SimplyBook’s interface reports a failed delivery, so from the admin side it looks fine.

The fix is not more logging — nobody reads logs that are usually empty. Make the callback announce itself. Have the handler post a short line to a chat channel you already watch (we relay ours into Slack) for every booking it receives: type, service, customer, start time. Once every real booking produces a visible message, silence becomes the alarm. If the diary has a booking in it and no message appeared, you know within minutes that the pipe is broken, rather than finding out when a customer arrives for an appointment you never saw.

The same announcement doubles as your test harness. Make a booking in the admin UI, watch for the message. That is the only proof that matters.

There is no reminder webhook

Worth knowing before you design your notification stack: SimplyBook fires no callback for reminders. The four notification types are all about the booking record changing. Reminders are purely time-based inside SimplyBook — they go out on its schedule, through its templates, and your server hears nothing.

If you want your own reminder emails, with your own wording and your own branding, you have to schedule them yourself from the booking start time when the create callback lands. That means storing the start time, computing your own send times, and re-checking the booking’s current status before each send so you do not remind someone about an appointment they cancelled last week. It is not difficult, but it is entirely on you, and it is not obvious from the documentation.

We built all of this for our own booking system at 365 Techies in July 2026 — the proxy, the authenticated callback, the queue, the reminders — because we wanted our customers to deal with us rather than with someone else’s booking widget. If you are wiring SimplyBook into your own site and would rather not rediscover these traps one lost booking at a time, we are happy to help.

// GOOD QUESTIONS

Frequently asked

Where is the callback URL set?

In SimplyBook admin under Settings → API, in a single “Callback URL” box. It is empty by default, and nothing anywhere warns you that your integration is therefore deaf to every booking made inside SimplyBook.

Is the callback signed?

No. The POST carries booking_id, booking_hash, company and notification_type, with no signature. You must authenticate it yourself — a secret token in the query string compared in constant time, plus a check that the company matches yours.

Does SimplyBook send a webhook for reminders?

No. Reminders are purely time-based, so there is no event to hook. If you want your own reminder emails you must schedule them yourself from the booking start time.

It looks configured but nothing arrives. What now?

Check whether your host’s bot-protection is intercepting server-to-server POSTs before they reach your script — that is a common and completely silent cause. The durable fix is to make every received callback announce itself somewhere you look, so that silence becomes the alarm.

Thinking about your own booking experience?

We built ours on SimplyBook and it is live on this site — have a look, then tell us what you are trying to do. Quoted per project, honestly, after a proper look.

01202 775566 · help@365techies.co.uk · MON–FRI 9AM–5PM