nisebookings docs

Concepts

How the booking engine thinks. Read this before writing an integration; it prevents most wrong assumptions about slots, capacity, money and time.

Contents

The shape of the system

                       ┌─────────────────────────── admin UI (/app, login)
account ─► catalogue ──┤
(key)      services    │
           staff       └──► availability engine ──► slots ──► order ──► payment
           schedules                                            │
           holidays                                             └──► notifications (mail)
                                    ▲
     public API / widget ───────────┘   (no login, public key, server-side re-check)

Your account (tenant)

An account is one business (the API calls it a tenant): name, slug, public key (the credential for the public API), time zone, locale, currency, VAT mode, deposit mode, gateways, booking rules (slot length, minimum notice, booking horizon, default status), calendar options and feature switches (cart, chain, recurrence, waiting list, combined-price method, occupancy display).

All data belongs to one account: a business can only ever see its own records, and every public call is bound to exactly one account by its key. A person relates to an account with a role (owner, admin, front_desk, staff); there is no account switcher — the first membership wins.

Catalogue: services, staff, extras

Category — grouping for the customer-facing list (Knippen, Verhuur, …). Optional.

Service — the thing being booked. Four types:

Type Meaning Example
simple one appointment, one staff member haircut, intake, rental hour
compound a fixed sequence of parts, one after another, with optional spare time between them wash → cut → dry
collaborative parts that must happen at the same time, each by a different staff member skipper + boat; two instructors
package a prepaid bundle of N appointments of one simple service 5-ride card

A simple service carries: duration, optional own slot length, price, deposit rule, VAT rate, minimum and maximum persons (capacity), padding before/after, staff preference for "any", visibility (public/private), one_booking_per_slot, which recurrence frequencies customers may choose, and an optional waiting-list cap. Prices and capacity can be overridden per staff member (staff_services), which is why a slot is always evaluated per (service, staff) pair.

Staff — anything with its own schedule that can be booked: a person, a boat, a room, a device. Staff carries a weekly schedule (per weekday start/end plus breaks), holidays/vacation days, contact details, visibility (public/private/archive), and the list of services performed. A staff record needs no login; a login is linked via staff.user_id when the person should see their own agenda.

Extras — optional add-ons per simple service: price, extra duration (added after the appointment), minimum/maximum quantity. A minimum of 1 makes the extra mandatory. Extras belong to simple services; on compound/collaborative services every extra is attached to the part that owns it.

Schedule and holidays — a service's availability comes from the staff schedules. Company opening hours are only a template for new staff rows; company holidays apply to everyone, staff holidays only to that staff member. A "toch open" holiday row (kind exception) makes the company open on a yearly closing day.

When is a time bookable?

A slot is a start time the engine may offer. For the chosen service and each candidate staff member:

  1. The start time lies on the slot grid: step = service.slot_length or the account-wide slot_length (default 15 min).
  2. The whole block fits in the schedule: duration + extras + padding before/after must be inside a working period and outside breaks, on that weekday.
  3. The date is not a holiday for the staff member or the company.
  4. The start is at least min_prior_minutes from now and within the booking horizon (max_days, default 365).
  5. It does not conflict, with padding, with an existing blocking appointment of that staff member (plus events they are linked to, plus holds while a customer is mid-checkout).
  6. Capacity allows it: the number of persons already booked (blocking statuses only) plus the requested persons must fit the staff member's capacity for that service.

A block that is partly occupied is offered as a partial slot when the remaining capacity suffices (group slots; the UI shows the occupancy). A full slot is omitted, unless a waiting list is open — then it is returned with state: "waitlist".

Two services can be booked into the same appointment (group join) only when the service, start, duration and extras match and the capacity still allows it; one_booking_per_slot forbids any join.

When several staff members qualify and the customer sends staff: "any", the service's staff preference decides: order (list order), least_expensive, most_expensive.

For compound/collaborative services the engine searches the whole sequence at once: parts run consecutively (compound, with spare time free for others) or in parallel on distinct staff members (collaborative). Only the first part's staff is customer-choosable.

Appointments, bookings, orders

order (one checkout)                      ← orders row, one token, one payment
 ├── appointment 09:30–10:15 (service A, staff X)   ← a block of time in a calendar
 │    ├── booking: Ada, 1 person, approved
 │    └── booking: Bob, 2 persons, approved          ← group join, same appointment
 ├── appointment 10:15–11:00 (service B, staff Y)   ← chain part
 └── series: appointments on 7 dates                ← recurring occurrences

Blocking statuses are pending and approved — those (and appointments with no bookings at all, which block entirely) determine occupancy.

Statuses

Status Dutch label Blocking Meaning
pending In afwachting yes booked, awaiting confirmation (when the business requires approval, or a payment is open)
approved Bevestigd yes confirmed
waitlisted Wachtlijst no wants a seat if one frees up; never paid, never blocking
cancelled Geannuleerd no cancelled by customer or admin
rejected Afgewezen no refused by admin, or payment failed/expired
done Afgerond no the appointment happened (manual or via automatic status change)

An account-level automatic status change can move appointments that have ended from one status to another (e.g. approved → done) automatically, checked every minute; bookings changed in the last two days get a notification.

Special booking kinds

Money

Price sources. The unit price of an item is the staff-specific price (staff_services.price) when that staff member was assigned, otherwise the service price. Compound/collaborative services use their own price, or the sum of the parts' staff prices when the business chose Optelsom van de onderdelen.

Order formula: per item unit price × persons, plus extras (× persons when the account multiplies them), summed to a subtotal; deposit applied per item ('100%', or '30%', or a fixed amount per person); coupon discount and fixed deduction; optional gateway surcharge (Mollie % + fixed); VAT. Prices include VAT by default (NL B2C): the shown total already contains it and the quote returns tax as the part that is VAT. Rounding is half-away-from-zero at 2 decimals.

What the customer pays now (payNow) is the deposit, or the full amount, depending on the account's deposit mode (deposit_only, choose_deposit, choose_full) and the customer's choice. The rest is due, payable on site. A zero order becomes a free order; a Mollie order whose payable amount is zero falls back to a local one.

Payment types. local (pay at the desk — the admin completes it later), free (nothing to pay), mollie (online; iDEAL/card on Mollie's page). One payment per order. Statuses: pending, completed, rejected, refunded; late arrivals are flagged (late_paid) when the slot was already given away.

Coupons — percentage and/or fixed amount, usage limit (account-wide), once-per-customer, validity dates (the booking date counts), minimum/maximum appointments per order, and optional restrictions to services/staff. A coupon use is given back when the payment is rejected.

Notifications

Mail is driven by templates per type: new_booking, status_changed, reminder, waitlisted, waitlist_offer, package_new, event_tickets. Each template picks recipients (customer, the staff member, admins, extra addresses), a status filter, a service filter and — for reminders — a timing (relative: N hours before the start; or a fixed hour, N days before). Bodies use {codes} and {#if …} blocks. Rendering happens when the mail is queued; sending retries with backoff. Order mails are sent once per order (staff get one per own appointment); status changes and reminders stay per booking. See 06-notifications.md.

Time and locale

Terminology: Dutch UI and English

Dutch (UI) English (docs)
dienst service
medewerker staff (member)
categorie category
afspraak appointment
boeking booking
bestelling order
reeks series
wachtlijst waiting list
pakket package
evenement / deelnemer event / attendee
tickettype ticket type
rooster schedule
vrije dag day off / holiday
openingstijden opening hours
pauze break
toeslag surcharge
aanbetaling deposit
kortingscode coupon
opmerking note (customer's)
interne notitie internal note
extra('s) extras
tijdstap slot length / time step
bezetting occupancy