nisebookings docs

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

  1. Create an account and complete the onboarding: Bedrijfsnaam, Webadres (a slug) and Tijdzone. This creates your account and makes you owner.
  2. Add what can be booked: Medewerkers (staff/resources, each with a weekly schedule) and Diensten (services, and which staff perform them).
  3. 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.

  1. 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-service and data-staff work the same way.
  2. 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

  1. Complete a booking through your page. The calendar (Agenda) shows it immediately.
  2. Afspraken shows the booking status; Betalingen shows the payment (if priced).
  3. Meldingen → Verzendlog shows the confirmation mail and its delivery status.
  4. 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