Quickstart — add bookings to a site
The fastest path: get a public key, paste two lines of HTML, done. Every route below is a live integration path; pick one.
| You want | Path | Time |
|---|---|---|
| Bookings on a normal website | Widget | minutes |
| Bookings on a WordPress site | WordPress | minutes |
| Your own UI / headless | Public API | hours |
| Ticket sales for an event | Events widget | minutes |
| Just test, no account yet | Sandbox: curl -X POST https://app.nisebookings.com/api/sandbox |
seconds |
Get a key
- Create an account and complete the onboarding: Bedrijfsnaam, Webadres (a slug) and Tijdzone. This creates your account and makes you owner.
- Add what can be booked: Medewerkers (staff/resources, each with a weekly schedule) and Diensten (services, and which staff perform them).
- Copy the Publieke sleutel from Instellingen → Insluiten. That key is the only credential the browser-facing side needs.
Optional but recommended before going live: Instellingen → Bedrijf en e-mail (sender name, reply-to), Instellingen → Betalingen (VAT, deposit, Mollie key), Instellingen → Insluiten → Toegestane websites (restrict who may embed your widget).
Let an agent configure your account
Everything above can also be done from code: Instellingen → API creates a key with scopes, and the management API then builds the whole catalogue — categories, services, staff, per-staff prices, the working week, opening hours and holidays. Nothing to click.
export API=https://app.nisebookings.com/api/v1 KEY=nk_…
curl -s -H "Authorization: Bearer $KEY" $API/me
curl -s -H "Authorization: Bearer $KEY" -X POST $API/services \
-d '{"title":"Knippen","duration":30,"price":25,"visibility":"public"}'
The quickstart in ../api/README.md walks an empty account to a real booking, and
the OpenAPI spec covers every endpoint.
Prefer to ask in plain language? The same key connects your coding agent to the hosted MCP server:
claude mcp add --transport http nise-booking https://app.nisebookings.com/api/mcp \
--header "Authorization: Bearer $KEY"
Path A: widget on any website
<script src="https://app.nisebookings.com/widget.js" defer></script>
<nise-booking data-key="<your public key>"></nise-booking>
That is the whole integration. The element renders the full flow — service, time, details, payment — inside a shadow DOM, styled with the page's font. Optional attributes:
<nise-booking
data-key="…"
data-api="https://app.nisebookings.com" <!-- needed only when widget.js is hosted elsewhere -->
data-service="<service id>" <!-- preselect a service -->
data-staff="<staff id|any>" <!-- preselect a staff member -->
data-lang="nl|en" <!-- widget language; default: the account's -->
></nise-booking>
Details, styling tokens and limitations: 03-widget-reference.md.
Path B: WordPress
No plugin needed; this works on any WordPress site where you can add HTML.
- Edit the page, add a Custom HTML block and paste the two lines from Path A (with your public
key). Optional attributes such as
data-serviceanddata-staffwork the same way. - Add the WordPress site to Toegestane websites in the booking app, or leave the list empty.
Your WordPress user must be allowed to add scripts (administrators are, by default).
Path C: your own front-end
const KEY = '<your public key>';
const API = 'https://app.nisebookings.com/api/public/' + KEY;
const { tenant, services } = await fetch(`${API}/services`).then((r) => r.json());
// tenant.timeZone, tenant.currency, tenant.gateways, tenant.depositMode, …
const { days } = await fetch(`${API}/slots?service=${serviceId}&from=2026-10-14&days=7&persons=1`)
.then((r) => r.json());
const slot = days.flatMap((d) => d.slots)[0];
const res = await fetch(`${API}/bookings`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
items: [{ service: serviceId, staff: slot.staffId, start: slot.start, persons: 1 }],
name: 'Ada Jansen', email: 'ada@example.com', gateway: 'local',
}),
});
const order = await res.json(); // order.orderToken, order.items[0].status
Full endpoint reference, error codes and recipes (Mollie checkout, recurring series, waiting list,
tickets): 04-public-api.md. Machine-readable: openapi.yaml.
Booking events instead of appointments
<script src="https://app.nisebookings.com/events.js" defer></script>
<nise-events data-key="<your public key>"></nise-events>
Publish an event with ticket types in Evenementen, then embed. See
03-widget-reference.md.
Try it first
No account needed: one call creates a sandbox — a complete test business with a
public key and an all-scopes management key, valid for 24 hours. It never sends mail (read what it
would have sent via GET /api/v1/outbox) and its Mollie checkout is a fake page.
curl -s -X POST https://app.nisebookings.com/api/sandbox # → {publicKey, apiKey, expiresAt, endpoints}
To just look: https://app.nisebookings.com/demo.html (widget + event shop). Runnable code:
examples/ — headless.mjs runs against a sandbox key unchanged.
Verify your integration
- Complete a booking through your page. The calendar (Agenda) shows it immediately.
- Afspraken shows the booking status; Betalingen shows the payment (if priced).
- Meldingen → Verzendlog shows the confirmation mail and its delivery status.
- The confirmation mail contains a cancel link; open it and cancel to test the customer path.
Troubleshooting
| Symptom | Cause / fix |
|---|---|
| Widget shows "De agenda kan nu niet worden geladen." | Wrong key, or the API host is unreachable from the browser. Check the network tab: a 404 means the key is wrong. |
403 {"error":"origin_not_allowed"} |
The embedding origin is not in Toegestane websites. Add it, or clear the list. |
| Widget renders but lists no services | Your account has no services with visibility = public and at least one public staff member who performs them. Check Diensten and Medewerkers → Diensten. |
| No times offered | Staff schedules (or opening hours for new staff), company/staff holidays, minimum notice, booking horizon, and the slot grid. Try another day; check the staff's Rooster. |
| Times offered but booking fails with 409 | Someone else took the slot; re-fetch and pick again. |
Payment link fails / 502 payment_unavailable |
Mollie key missing or invalid, or Mollie temporarily unreachable. Check Instellingen → Betalingen. |
| Mails not arriving | Check Meldingen → Verzendlog for status and error text; a customer may also find it in their spam folder. |
| Widget shows an older version than expected | Rare — the console shows nisebookings@<version> on load; add ?v=<version> to the script URL (or hard-refresh) if a cache serves an old copy. |
Going live checklist
- Real services, staff schedules, holidays (company + per staff) entered.
- Instellingen → Algemeen: time zone, time step, minimum notice, booking horizon.
- Instellingen → Afspraken: new bookings confirmed immediately or pending approval?
- Instellingen → Betalingen: VAT mode, deposit mode, gateways, Mollie live key.
- Instellingen → Insluiten: allowed websites restricted to your domains.
- Notification templates reviewed in Dutch (sender name, reply-to, texts, reminders).
- Reminders, payment timeouts and automatic status changes are handled by the service — nothing to run yourself.
- A test booking, cancellation and refund done end to end.