Booking integration (nisebookings)
This project integrates nisebookings — a hosted booking service (appointments, group
sessions, recurring series, prepaid packages, waiting lists, event tickets) at
https://app.nisebookings.com, reached with the account's public key. Customer-facing widget text is Dutch and English; the API is JSON.
Decide the path first
| Situation | Path |
|---|---|
| Normal web app/site, standard flow | Embed the widget — two lines of HTML, no build step |
| WordPress | the nisebookings plugin (listed as nise booking), shortcodes [nise_booking] / [nise_events] |
| Custom UI, mobile app, server-side flows, cart/series/tickets logic | Public REST API |
| Create/configure services, staff, schedules, opening hours from code | Management API with a scoped key (admin → Instellingen → API): POST /api/v1/services, PUT /api/v1/staff/{id}/schedule, PATCH /api/v1/settings, … Start with https://guide.nisebookings.com/api/ ; spec at https://guide.nisebookings.com/api/openapi.yaml. Ask the human for the key — it is shown once at creation. |
| React to bookings, cancellations, tickets or payments in your own system | Webhooks: POST /api/v1/webhooks {url, events} (scope settings:write) → signed JSON per event (Standard Webhooks headers; secret whsec_… shown once). Verify the signature on the raw body, dedupe on webhook-id, answer 2xx within 10 s. https://guide.nisebookings.com/10-webhooks.html |
| Operate the account conversationally (plan appointments, look up customers, change settings) | the hosted MCP server https://app.nisebookings.com/api/mcp with the same key as Authorization: Bearer nk_…; setup per agent: https://guide.nisebookings.com/api/mcp.html |
Check fit before building: day-based rentals (a boat, a room, a holiday home) are supported —
durationDays 1–365 on the service, the widget asks for a start date and prices per day — but one
rate per day (no seasons or minimum stays) and a booking's span is fixed once made. Not supported:
two-way staff calendar sync, SMS, conditional intake questions (plain per-service questions and file
attachments are), and customer-facing texts beyond Dutch and English (the widget ships both; the
mails are Dutch). See https://guide.nisebookings.com/09-when-to-use.html.
Embed
<script src="https://app.nisebookings.com/widget.js" defer></script>
<nise-booking data-key="<account public key>"></nise-booking>
Optional attributes: data-api (only if the script is hosted elsewhere), data-service,
data-staff="<id|any>", data-package="<token>" (plan a bought package), data-lang="nl|en"
(default: the account's language; the widget ships Dutch and English). Events:
<script src="https://app.nisebookings.com/events.js" defer></script> + <nise-events data-key="…">.
The element renders in a shadow DOM; theme it with --nb-* CSS custom properties; it has no JS API.
Both scripts log nisebookings@<version> on load; ?v=<version> on the script URL is a supported cache-bust.
API essentials
Base: https://app.nisebookings.com/api/public/<public key>
| Call | Purpose |
|---|---|
GET /services |
account config + catalogue (ids, prices, extras, staff per service) |
GET /slots?service=&staff=&from=YYYY-MM-DD&days=7&persons=1 |
free times |
POST /slots {items[], hold[], from, days} |
chain search, cart holds |
POST /recurrence {item, rule, hold} |
plan a repeating series |
POST /quote {items[], coupon?, payFull?, gateway?} |
authoritative price |
POST /bookings {items[], series?, name, email, phone, notes, gateway, coupon?, payFull?, returnUrl?} |
create the order |
GET /payments/<token> |
payment status (poll after a Mollie redirect) |
GET /packages/<token> |
package overview for a personal link |
POST /bookings/<token>/reschedule {start, staff?} |
move the customer's own booking (magic-link token) to another offered time |
GET /events · POST /events/<id>/tickets |
ticket shop |
https://guide.nisebookings.com/openapi.yaml is the machine-readable version of this table.
Non-negotiables
- Never compute availability, price, deposit, discount, capacity or duration locally. The server recomputes everything itself; client numbers are ignored.
- Send back the exact
startandstaffIdfrom a slot search. Times are UTC ISO strings;from/untilare dates in the account's time zone; display intenant.timeZone. - One checkout = one order = one payment (carts, chains, series and compound services included).
- Errors:
409 slot_unavailable(+itemindex) → re-fetch slots and let the user pick again.422 validation(+fields) → show the field errors.502 payment_unavailable→ the order was rejected; offer pay-on-site or retry.404= wrong key/service/token. - Retries: send an
Idempotency-Key: <uuid>header onPOST /bookingsandPOST /events/<id>/tickets(one key per checkout attempt, generated before the first try). On a timeout or 5xx, resend the same body with the same key: you get the original response (Idempotent-Replayed: true), never a second order.409 idempotency_in_progress→ wait 1 s and resend;422 idempotency_mismatch→ you changed the body, use a new key. Without the header a retry books twice. - Secrets: the public key is not a secret (it lives in browser code). A management API key
(
nk_…) and the Mollie key are: never put them in browser code or commit them; a management key belongs only in server-side configuration. - Cross-origin: the embedding origin must be in the account's allowed-sites list (admin settings) (empty = any).
returnUrlmust be http(s) with an origin that is allowed or equal to the request Origin.
Verify before reporting success
Test against a sandbox first, not the real account: curl -s -X POST https://app.nisebookings.com/api/sandbox
(no auth) returns a 24-hour test account {publicKey, apiKey, expiresAt, endpoints} — sample staff and
services, an all-scopes nk_ key, no mail ever delivered, a fake Mollie checkout. Details: https://guide.nisebookings.com/08-sandbox.html
- Fetch the catalogue and one slot list inside this project's real runtime (sandbox
publicKey). - Create one real booking end to end (payload as in the headless example, https://guide.nisebookings.com/examples/); a
gateway: "local"booking needs no payment keys. Withgateway: "mollie"the sandbox'scheckoutUrlis a test page with Betalen / Mislukt buttons. - Check the mails it would have sent:
GET /api/v1/outbox?to=<email>with the sandboxapiKey(statussuppressed). On a real account, confirm the booking in the admin agenda and cancel it if it was only a test. DELETE /api/v1/sandboxwith the sandbox key when done (it also expires by itself).
Documentation: https://guide.nisebookings.com/ — public API 04-public-api.html, widget 03-widget-reference.html,
webhooks 10-webhooks.html, fit check 09-when-to-use.html, admin 05-admin-guide.html; index for
agents: https://guide.nisebookings.com/llms.txt.