# Booking integration (nisebookings)

This project integrates **nisebookings**, a hosted booking service (appointments, groups,
recurring series, packages, waiting lists, event tickets). It runs at `https://app.nisebookings.com` and is reached through the account's public key;
there is no login on the customer side.

## Two APIs: the **public** one (no auth, `data-key`, creates bookings) and the **management** API
(`/api/v1`, scoped `nk_…` key created under Instellingen → API, server-side only) that configures
settings, opening hours, categories, services, staff, schedules and holidays. Ask the human for the
key; never put it in browser code. The same key also works for the hosted MCP server
(`https://app.nisebookings.com/api/mcp`, `.vscode/mcp.json` setup: https://guide.nisebookings.com/api/mcp.html).

## Rules

- The booking backend owns all logic. Never compute availability, durations, prices, deposits,
  discounts or capacity in this project — always ask the API.
- Never trust or reuse a price the user typed: `POST /bookings` and `POST /quote` recompute everything
  server-side, and only their numbers are authoritative.
- Send back the exact `start` and `staffId` a slot search returned. Do not construct timestamps.
- All API instants are UTC ISO strings; `from`/`until` are `YYYY-MM-DD` dates in the account's time zone.
  Display times in the `timeZone` returned by `GET services`.
- Handle `409 {"error":"slot_unavailable"}` by re-fetching slots and letting the user pick again.
  Handle `422 {"error":"validation","fields":{…}}` by showing the field errors.
- One checkout = one order = one payment, even for carts, chains, series and compound services.
- The public key is not a secret (it ships in browser code). The management key (`nk_…`) and payment
  keys are: keep them server-side and out of version control.

## Two integration paths

Embed (preferred for a normal web app; no build step, no dependencies):

```html
<script src="https://app.nisebookings.com/widget.js" defer></script>
<nise-booking data-key="<account public key>"></nise-booking>
```

Event tickets: `<script src="https://app.nisebookings.com/events.js" defer></script>` +
`<nise-events data-key="…">`. Add `data-lang="en"` (or `"nl"`) to either element to override the
account's language, which is the default; the widgets ship Dutch and English. Both scripts log `nisebookings@<version>` on load; `?v=<version>` on
the script URL is a supported cache-bust. WordPress: the nisebookings plugin (listed as **nise booking**), shortcodes `[nise_booking]` and
`[nise_events]`.

Headless (custom UI): `GET /api/public/<key>/services` → `GET …/slots?service=&from=&days=` →
`POST …/quote` → `POST …/bookings`. Chain/cart: `POST …/slots` with `items[]` and `hold[]`.
Recurring: `POST …/recurrence` then book every occurrence with the same `series` key plus a
`series: { "<key>": rule }` map. Mollie checkout: send `gateway: "mollie"` + `returnUrl` (its origin
must be in the account's allowed sites), redirect to `payment.checkoutUrl`, then poll
`GET …/payments/<token>` every 2 s until the status is not `pending`.
Retries: send `Idempotency-Key: <uuid>` on `POST …/bookings` and `POST …/tickets` and resend the same
body with the same key after a timeout or 5xx — you get the original order back, never a second one.
Events in your own backend (booking created/cancelled, payment completed, …): register a webhook with
`POST /api/v1/webhooks`, verify the Standard Webhooks signature, dedupe on `webhook-id`
(https://guide.nisebookings.com/10-webhooks.html).

## Verify before claiming success

Use a throwaway **sandbox** account for this, not the real one: `POST https://app.nisebookings.com/api/sandbox`
(no auth) → `{publicKey, apiKey, expiresAt}`, valid 24 h, no mail delivered (read it via
`GET /api/v1/outbox`), fake Mollie checkout. Details: https://guide.nisebookings.com/08-sandbox.html

1. Load the catalogue and one slot list in this project's actual runtime.
2. Make one real booking end to end (a local/pay-on-site booking works without payment keys).
3. Confirm the result in the admin agenda, and cancel the test booking afterwards if it was not wanted.

## Reference

- Full API, error codes and recipes: https://guide.nisebookings.com/04-public-api.html and https://guide.nisebookings.com/openapi.yaml.
- Widget attributes, flows, theming and limits: https://guide.nisebookings.com/03-widget-reference.html.
- Fit check (when this service is the wrong choice): https://guide.nisebookings.com/09-when-to-use.html.
