Skip to content
StippaStippa
FeaturesPricingDemoFAQContact
Log inGet started

Getting started

  • Introduction
  • Authentication
  • Permissions

Core concepts

  • Errors
  • Pagination
  • Idempotency
  • Rate limits

Resources

  • Appointments
  • Customers
  • Services
  • Staff
  • Availability
  • Products
  • Payments
  • Webhooks

Recipes

  • Recipe: book from your own website
  • Recipe: sync the diary into a spreadsheet or BI tool
  • Recipe: a Slack message on every new booking

Reference

  • Changelog
  • OpenAPI 3.1 document

Availability

The only computed resource here: what is bookable, why two identical requests can differ, and how the two endpoints agree.

Last updated 1 August 2026

Availability is the one resource on this API that is computed rather than stored. There is no availability table. Every response is worked out at request time from the staff roster, the recurring breaks, the schedule exceptions, the appointments already booked, and the business's own booking rules.

Two consequences follow, and both of them are the whole content of this page: two identical requests can legitimately return different answers, and you must not cache the result.

MethodEndpointScopePlan
GET/api/v1/availability/slots

Bookable start times for one date

Query: serviceIddatestaffMemberId

availability:readGroei
GET/api/v1/availability/dates

Bookable dates in a window

Query: serviceIdfromtostaffMemberId

availability:readGroei

Two endpoints, two questions

They exist as a pair because a booking interface asks two different questions and answering the second with the first is expensive.

GET /api/v1/availability/dates answers "which days in this window have anything at all". That is the month calendar: which dates are clickable.

GET /api/v1/availability/slots answers "what times are free on this one day". That is the time picker you show after somebody clicks a date.

curl "https://stippa.nl/api/v1/availability/dates?serviceId=b50661da-7649-4c14-b748-91bbee8e9643&from=2026-08-17&to=2026-08-23" \
  -H "Authorization: Bearer $STIPPA_KEY"
{
  "from": "2026-08-17",
  "to": "2026-08-23",
  "serviceId": "b50661da-7649-4c14-b748-91bbee8e9643",
  "staffMemberId": null,
  "timezone": "Europe/Amsterdam",
  "durationMinutes": 15,
  "dates": [
    { "date": "2026-08-17", "slotCount": 28 },
    { "date": "2026-08-18", "slotCount": 28 },
    { "date": "2026-08-19", "slotCount": 28 },
    { "date": "2026-08-20", "slotCount": 28 },
    { "date": "2026-08-21", "slotCount": 28 },
    { "date": "2026-08-22", "slotCount": 32 }
  ]
}

A date with nothing bookable is absent from the array rather than present with a zero. That window is seven days and the response has six entries: the Sunday is closed, so it is simply not there. Do not index the array by day offset. Build a set of the dates you were given and treat every other day in your window as unavailable.

slotCount is there so a month view can show "3 left" without a request per day. It is a count and not a promise: by the time somebody picks that date, it may be a different number.

The two endpoints agree, and that is load bearing

slots for a date returns exactly as many entries as that date's slotCount in dates. Asking for 2026-08-20 above:

curl "https://stippa.nl/api/v1/availability/slots?serviceId=b50661da-7649-4c14-b748-91bbee8e9643&date=2026-08-20" \
  -H "Authorization: Bearer $STIPPA_KEY"
{
  "date": "2026-08-20",
  "serviceId": "b50661da-7649-4c14-b748-91bbee8e9643",
  "staffMemberId": null,
  "timezone": "Europe/Amsterdam",
  "durationMinutes": 15,
  "slots": [
    { "startTime": "2026-08-20T07:00:00.000Z", "endTime": "2026-08-20T07:15:00.000Z" },
    { "startTime": "2026-08-20T07:15:00.000Z", "endTime": "2026-08-20T07:30:00.000Z" },
    { "startTime": "2026-08-20T09:45:00.000Z", "endTime": "2026-08-20T10:00:00.000Z" },
    { "startTime": "2026-08-20T11:00:00.000Z", "endTime": "2026-08-20T11:15:00.000Z" }
  ]
}

Twenty-eight slots, matching slotCount: 28. The gap between 10:00 and 11:00 UTC is the business's lunch break, which is on the roster rather than being a booked appointment.

The agreement is worth stating because it has not always held. A month calendar and a time picker are two computations over the same inputs, and a business having a day off used to be subtracted in one of them and not the other, so a date was clickable and then had no times on it. They are one path now. If you ever see them disagree, that is a bug worth telling us about rather than a subtlety to work around.

Everything is UTC, and timezone is for display

startTime and endTime are UTC instants with a Z suffix, like every timestamp on this API. date, from and to are plain calendar dates with no zone, and they mean dates in the business's timezone, which the response tells you.

That combination is not sloppiness, it is the only pair that works. "Tuesday the 20th" is a local calendar concept and a salon's Tuesday does not start at midnight UTC. "09:00 with a 15-minute service" is an instant. Render the slots in timezone and your times will match what the business sees on its own screen; render them in the browser's zone and a customer booking from Spain will see times an hour off what the salon expects.

Do not cache this

⚠️

Availability is the one resource on this API with no cacheable answer. A slot that was free when you read it can be taken before your customer clicks it, and there is no expiry you can pick that makes a stale answer safe.

So do not treat a slot list as a reservation. There is nothing to hold and no endpoint that holds it: the way you find out whether a slot is really yours is by creating the appointment and handling 409 CONFLICT, which is a normal outcome of a correct booking flow rather than an error case. Fetch slots again and let the customer pick another. The booking recipe does exactly this.

Narrowing by staff member

Both endpoints take an optional staffMemberId. Without it, a slot is free if anybody bookable can do that service then, which is what a customer with no preference wants.

With it, you get one person's availability. Do that when the customer has chosen somebody, and then pass the same staffMemberId to the create along with staffLocked: true, so a later reschedule does not silently reassign them.

Asking for slots without a staffMemberId and then creating with one is the mismatch worth avoiding: the wider question was answered for the whole team, and the narrower create can still be refused.

On this page

  • Two endpoints, two questions
  • The two endpoints agree, and that is load bearing
  • Everything is UTC, and timezone is for display
  • Do not cache this
  • Narrowing by staff member
StippaStippa

Appointment scheduling, without the hassle.

For whom

  • For salons
  • Beauty salons
  • Physiotherapists
  • Coaches
  • Personal trainers

Features

  • Booking widget
  • Online payments
  • Reminders
  • No-show prevention
  • Online calendar
  • Client management

Product

  • Features
  • Pricing
  • FAQ
  • Contact
  • System status

Legal

  • Privacy policy
  • Terms of service
  • Data processing agreement

© 2026 Stippa. All rights reserved.

De Rechter Software · Molenwater 20, 4511 BN Breskens · KvK 98466402 · btw NL005332100B80

······