Skip to main content

Booking Self-service Widget — API reference

The Widget API is mounted under /index.php/apps/shillinq/api/widget/ and requires Authorization: Bearer <api_key> plus ?businessId=<id> on every request (REQ-WSW-001). Rate-limit: 100 req/min per business by default (configurable per WidgetAccessKey).

GET /api/widget/services

Returns the active services available for booking. Public-safe subset only — no internal fields or PII.

Query parameters

NameRequiredDescription
businessIdyesPartner business id, matches the access key.

Response — 200 OK

{
"services": [
{
"serviceId": "svc-001",
"name": "Intake consultation",
"duration": 30,
"price": 75.0,
"currency": "EUR",
"description": "30-minute introductory consultation."
}
]
}

Errors

StatusBodyCause
401{error: "unauthorised", message: "..."}Missing or invalid bearer token.
429{error: "rate-limited", message: "..."}More than 100 req/min for the key.
500{error: "server-error", message: "..."}Service catalogue unavailable.

GET /api/widget/slots

Returns the available appointment slots for a (service, resource, date) tuple.

Query parameters

NameRequiredDescription
businessIdyesPartner business id.
serviceIdyesLogical service id.
resourceIdyesLogical resource id.
dateyesCalendar date, ISO YYYY-MM-DD UTC.

Caching

The slot list is cached for 5 minutes per (service, resource, date). The response includes an ETag header; partners that send If-None-Match with the previous ETag receive 304 Not Modified when the cached list is unchanged.

Response — 200 OK

{
"date": "2026-05-22",
"slots": [
{ "startTime": "2026-05-22T09:00:00Z", "endTime": "2026-05-22T09:30:00Z" },
{ "startTime": "2026-05-22T09:30:00Z", "endTime": "2026-05-22T10:00:00Z" }
],
"cached": false
}

POST /api/widget/appointments

Creates an appointment from the widget. Returns 201 Created with the booking id and a confirmation message. Customer details are accepted on input but never echoed back; the appointment record stores an anonymous hash of the email so admins can deduplicate self-service bookings (design D6).

Request body

{
"serviceId": "svc-001",
"resourceId": "res-001",
"startTime": "2026-05-22T09:00:00Z",
"endTime": "2026-05-22T09:30:00Z",
"customerName": "Alice Smith",
"email": "alice@example.com",
"phone": "+31612345678",
"notes": "Wheelchair access required."
}

Response — 201 Created

{
"appointmentId": "apt-1a2b3c4d5e6f7080",
"status": "pending_confirmation",
"confirmationMessage": "Your appointment was created. You will receive a confirmation email shortly."
}

Errors

StatusBodyCause
400{error: "bad-request", message: "..."}Validation failure (REQ-WSW-006).
401{error: "unauthorised", message: "..."}Missing or invalid bearer token.
409{error: "slot-unavailable", message: ...}Slot was just booked by someone else.
429{error: "rate-limited", message: "..."}More than 100 req/min for the key.
500{error: "server-error", message: "..."}Booking persistence failed.