ArtisanPack UI
DocsBookingsREST API

REST API

Bookings v1.0.0

The public JSON API is the headless surface — a widget, a mobile app, or a bespoke frontend books against it, and it deliberately does without a session...

The public JSON API is the headless surface — a widget, a mobile app, or a bespoke frontend books against it, and it deliberately does without a session or CSRF token. Endpoints sit under api/ + public.route_prefix (default api/bookings) and are named artisanpack.bookings.api.*.

Public read endpoints

Method Path Name Returns
GET services …api.services.index Active services
GET services/{slug}/providers …api.services.providers Providers offering a service
GET services/{slug}/slots …api.services.slots Bookable slots for a service

Create a booking

Method Path Name Rate limit
POST / (i.e. api/bookings) …api.bookings.store bookings.rate-limit:post
POST /api/bookings
{
    "service": "discovery-call",
    "start_time": "2026-06-01T15:00:00+00:00",
    "customer_name": "Sam Rivera",
    "customer_email": "sam@example.test",
    "customer_timezone": "America/Chicago",
    "intake_data": { "goal": "Learn to juggle" }
}

Manage a booking by token

Mounted under manage/{token} and guarded by the bookings.manage-token middleware:

Method Path Name Rate limit
GET manage/{token} …api.manage.show manage_get + manage_token
POST manage/{token}/cancel …api.manage.cancel post
POST manage/{token}/reschedule …api.manage.reschedule post

The read returns the booking plus a meta block describing what the link may still do:

{
    "data": { "id": 41, "status": "confirmed", "start_time": "2026-06-01T15:00:00+00:00" },
    "meta": { "can_cancel": true, "can_reschedule": true, "changes_allowed_until": "2026-05-31T15:00:00+00:00" }
}

A reschedule answers 409 when the slot was taken since the page was drawn. An unknown, malformed, or wrong-site token all answer with the same 404 and message. See Manage Tokens.

iCal feeds

Under public.route_prefix without the api/ prefix (default bookings/ical), named artisanpack.bookings.ical.*:

Method Path Name
GET ical/providers/{token}.ics …ical.provider
GET ical/customers/{token}.ics …ical.customer

Both support conditional requests (ETag / If-None-Match304). See iCal Feeds.

The no-JS widget target

The Livewire widget's plain-form fallback posts to a web-group route (default bookings/widget), named artisanpack.bookings.widget.store, rate-limited on the post bucket. This is not part of the JSON API — it needs the session and CSRF the API does without. See Public Booking Widget.

Rate limiting & trusted proxies

Every public route carries a named rate-limit bucket, most keyed by client IP. Behind a proxy you must configure Laravel's trusted proxies or every customer shares one bucket. See Rate Limiting and Trusted Proxies.

The typed client

@artisanpack-ui/bookings-js ships a typed BookingsClient over this surface — see Headless Client.