// SIMPLYBOOK API

Six SimplyBook traps that bite you in production

One booking that quietly became twelve. Calendar invites an hour out all summer. A webhook that was never connected. Every one of these cost us time on a live system — symptom, cause and fix.

  • ● All found live
  • ● Symptom → cause → fix
  • ● Nothing theoretical

Traps, not bugs

Everything below behaved exactly as SimplyBook intended. Each one still cost us time, because the behaviour was invisible until it landed in production. This is what we found building our own booking integration in July 2026 — a PHP proxy in front of SimplyBook’s JSON-RPC API, with our own front end. Symptom first, then cause, then what we actually did about it.

1. One booking creates twelve

Symptom. We booked a single test appointment. The diary filled with twelve appointments, out to November 2027. Our confirmation email told the customer about one of them.

Cause. The service was configured as recurring. When a recurring service is booked, a single book() call returns the whole series — the response’s bookings array contains every occurrence, not just the one the customer clicked. Our code read bookings[0], logged it, and threw the rest away. Nothing errored. The extra eleven existed only in SimplyBook’s diary, invisible to us.

Fix. Treat the response as a list, always — even for services you believe are one-off, because whether a service recurs is a setting somebody can change in the admin UI without touching your code. Iterate the full array. Store every occurrence with its own id. Tell the customer plainly on the confirmation screen and in the email: how many appointments, and the date of the last one. Then queue a reminder per occurrence, not one for the booking.

$res = $rpc->book(...);
foreach ($res['bookings'] as $b) {
    store_occurrence($b);   // not just $res['bookings'][0]
    queue_reminder($b);
}

If your customer only ever wanted one appointment, this is the difference between finding out at booking time and finding out when they ring up about the eleven reminders.

2. Calendar invites land an hour out

Symptom. The confirmation email looks right. The customer taps the calendar attachment and the appointment saves an hour away from the time on the page.

Cause. SimplyBook attaches an iCal file to its emails, and there is a separate setting — “Include TZ into iCal” — which is off by default. With it off, the .ics carries no timezone information at all. The receiving calendar has to guess, and through British Summer Time it guesses wrong. In winter you may never notice; from late March you get a steady trickle of people arriving an hour early or an hour late.

Fix. Turn that setting on. If you generate your own .ics — we do, because we send our own emails — do it properly: include a real VTIMEZONE block for Europe/London with the last-Sunday-in-March and last-Sunday-in-October DST rules, and write the start as DTSTART;TZID=Europe/London: rather than a bare local time. A floating local time in an .ics is a bug waiting for the clocks to change.

3. Nothing reaches your server

Symptom. The integration works perfectly in testing, because everything you book you book through your own code. Then someone takes a booking inside SimplyBook — over the phone, or by editing the diary — and your system never hears about it. No reminder, no record, no email.

Cause. The Callback URL box, in SimplyBook admin under Settings → API, is empty by default. Nothing warns you. There is no “webhooks not configured” banner, no failed-delivery log to check, because there is nothing to deliver to. Silence looks identical to success.

Fix. Fill the box in, then prove it fires. SimplyBook POSTs raw JSON containing booking_id, booking_hash, company and notification_type (one of create / cancel / notify / change). Three things to get right:

  • It is not signed. Protect the endpoint yourself. We put a secret token in the query string and compare it with hash_equals(). Booking detail is then read back with getBookingDetails(id, sign) where sign = md5(bookingId . bookingHash . secretKey).
  • Return a non-2xx on transient failures so SimplyBook retries. If your lookup fails and you still return 200, that booking is gone for good — you have told SimplyBook the delivery succeeded.
  • Configured does not mean working. Hosting bot-protection (SiteGround’s, in our case) can intercept server-to-server POSTs before your script ever runs. Make the callback announce itself — ours posts to Slack — so silence becomes something you notice rather than something you assume.

Related: SimplyBook fires no webhook at all for reminders. They are purely time-based. If you want your own reminder emails, schedule them yourself from the booking start time.

4. Client creation refused, with no field named

Symptom. addClient fails every time with “Value is required and can’t be empty”. You are sending name, email and phone. You add address, city, country. Same error. Nothing tells you what is missing.

Cause. Two admin settings block API client creation, and neither appears in the documented parameter list. Required custom Client Fields (Custom Features → Client Fields; on newer accounts the screen is called “Intake form for clients”, though SimplyBook’s docs describe the older UI) and Mandatory registration fields (Settings → Email and SMS settings). The documentation for the company administration service states that addClient accepts only name/email/phone/address1/address2/city/zip/country_id, and that custom Client Fields have no API surface. That is wrong.

Fix. Read the raw error. The live JSON-RPC failure is code -32070, and its payload contains data.field set to client_fields/<32-character hex id> — naming the exact field that is missing. Most client libraries truncate error.data, which is why this looks like a dead end. Log the whole error object. Then pass a map keyed by those ids inside the client-data object:

client_fields: { "<32-hex id>": "value" }

We verified this live: a dry-run addClient succeeded once it auto-filled the one required custom field. Note that getCompanyParam('require_fields') tells you which built-in fields are required (ours returned “email”) and says nothing about custom ones — so it will not save you here.

5. Hiding a category does not hide its services

Symptom. You tidy the diary by hiding a whole category — internal jobs, staff time, a service you are not selling yet. It disappears from the view you are looking at. It is still publicly bookable.

Cause. Per-service visibility is the only setting that governs whether a service can be booked. Category visibility affects presentation, not access. A service you believe is private stays reachable.

Fix. Set visibility on every service individually, and never rely on the category as a container. Then verify from outside your own session: list services through the public API with a plain public token, as an anonymous visitor would, and diff that list against what you intend to sell. We expose service listing through our proxy as a public read-only endpoint anyway, so this check is one request. Do it after every batch of admin changes, because it is the kind of thing that quietly regresses.

6. A client login is not proof of custom

Symptom. You use SimplyBook client accounts as the sign-in for something that matters — a portal, a discount, a paid feature — and people who have never paid you anything have access.

Cause. SimplyBook client accounts are self-registerable with any email address. A successful client login proves exactly one thing: that person controls that booking account. It is not evidence of a relationship, a payment, or an entitlement.

Fix. Never grant paid entitlements from a booking login. Keep identity and entitlement separate: authenticate however you like, then check entitlement against your own records of who is actually paying. We use our own passwordless emailed-code sign-in rather than SimplyBook’s, and signing in never grants a paid tier on its own.

One deliverability note, since emailed codes only work if they arrive: PHP’s mail() without the -f envelope-sender parameter lets Return-Path default to the hosting account, SPF then authenticates the wrong domain, and Gmail junks or rejects the message. That single missing parameter stopped our sign-in codes reaching Gmail. We send through authenticated SMTP with an explicit envelope sender now.

The pattern

Five of these six are silent. No exception, no red banner, no entry in a log you were already reading. The defence is the same each time: read whole responses rather than first elements, read whole error payloads rather than messages, and make the quiet paths announce themselves so that nothing happening looks different from everything working.

We built this for our own booking system at 365 Techies — the diary runs on SimplyBook, but our customers only ever see our own pages, emails and sign-in. If you are wiring SimplyBook into something of your own and would rather not rediscover the above, we are happy to talk it through.

// GOOD QUESTIONS

Frequently asked

Why did one booking create twelve appointments?

The service was configured as recurring. A single book() call then returns the entire series in the response’s bookings array. If your code reads only the first entry, eleven further appointments exist in the diary that neither you nor the customer has been told about.

Why are calendar invites an hour out?

There is a separate “Include TZ into iCal” setting, off by default, which leaves the attached .ics file with no timezone at all. Through British Summer Time that lands appointments an hour wrong on some devices.

I hid a service category but the services still show. Why?

Category visibility does not cascade. Per-service visibility is the only thing that counts, so services you believed were private can remain publicly bookable.

Is a SimplyBook client login proof someone is my customer?

No. Client accounts are self-registerable with any email address, so a login proves only that someone controls that booking account. Never grant paid entitlements based on one.

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