# Simple Bookings — Full API Reference > Simple Bookings is a Danish multi-tenant appointment booking platform. This document is the full LLM-ingestible API reference. The concise index lives at https://simple-bookings.dk/llms.txt. Operated by Beaconware ApS (CVR 25469879), brand "Cura AI". Marketing site: https://simple-bookings.dk. Application host: https://app.simple-bookings.dk. All API endpoints are mounted under `/api/`. ## Conventions ### Base URL ``` https://app.simple-bookings.dk/api/ ``` ### Authentication Bearer JWT tokens in the `Authorization` header: ``` Authorization: Bearer ``` Two distinct auth systems, not interchangeable: | System | Middleware | JWT payload | Used by | | -------------- | -------------------- | ------------------------------------------------ | -------------------------- | | Practitioner | `authenticateToken` | `{ userId, email, role }` | Admin, Staff, Supporter | | Client portal | `authenticateClient` | `{ clientId, email, type: 'client' }` | Customer portal | SSE/EventSource endpoints accept `?token=` as query param because EventSource cannot set custom headers. ### Content types and formats - Default Content-Type: `application/json` - CSV export and iCal download endpoints return their respective MIME types - Timestamps: ISO 8601 strings (UTC) - Money: integers in **øre** (1 DKK = 100 øre) - Booking status: uppercase strings — `CONFIRMED`, `CANCELLED`, `COMPLETED` ### Error format ```json { "error": "Human-readable message", "code": "OPTIONAL_MACHINE_CODE", "details": { "field": "info" } } ``` Common status codes: 400 validation, 401 unauthorized, 402 payment required, 403 forbidden, 404 not found, 409 conflict (e.g. double-booking), 422 unprocessable, 429 rate-limited, 500 server error. ### Rate limits Per-IP and per-token rate limits apply. 429 responses include `Retry-After` in seconds. ## Endpoint Catalogue The Simple Bookings codebase exposes 421+ endpoints across 65+ route files. The catalogue below covers the public, stable surface most useful for integrators. See the route files in `server/src/routes/*.ts` for the full list. ### Auth — /api/auth - `POST /api/auth/register` — Create a new practitioner account (beta-gated) - `POST /api/auth/login` — Body: `{ email, password }` → `{ accessToken, refreshToken, user }` - `POST /api/auth/refresh` — Body: `{ refreshToken }` → new access token - `POST /api/auth/logout` — Invalidate refresh token - `GET /api/auth/google` — Begin Google OAuth flow - `GET /api/auth/google/callback` — OAuth callback - `POST /api/auth/forgot-password` — Request reset email - `POST /api/auth/reset-password` — Body: `{ token, newPassword }` - `GET /api/auth/me` — Current user profile ### TOTP / 2FA — /api/totp - `POST /api/totp/enroll` — Begin TOTP enrollment, returns QR data - `POST /api/totp/verify` — Verify code during enrollment or login - `POST /api/totp/disable` — Disable TOTP (requires current code) - `GET /api/totp/status` — Whether TOTP is enabled for current user ### Sites — /api/sites - `GET /api/sites` — List sites accessible to current user - `POST /api/sites` — Create a site (onboarding) - `GET /api/sites/:siteId` — Site detail - `PATCH /api/sites/:siteId` — Update site - `DELETE /api/sites/:siteId` — Soft-delete site - `GET /api/sites/:siteId/business-hours` — Weekly business-hours config - `PUT /api/sites/:siteId/business-hours` — Replace business hours - `GET /api/sites/:siteId/closures` — Date-range closures (holidays, vacation) - `POST /api/sites/:siteId/closures` — Create closure - `DELETE /api/sites/:siteId/closures/:id` — Delete closure > **Site ID gotcha:** `site.id` is a UUID used by Prisma. `site.siteId` is the public slug used in API routes. Use the slug in URLs. ### Services — /api/sites/:siteId/services - `GET /api/sites/:siteId/services` — List services for site - `POST /api/sites/:siteId/services` — Create service - `GET /api/sites/:siteId/services/:id` — Service detail - `PATCH /api/sites/:siteId/services/:id` — Update service - `DELETE /api/sites/:siteId/services/:id` — Archive service Service shape: `{ id, name, duration (minutes), bufferAfter, price (øre), currency: 'DKK', description, staffIds, color }`. ### Staff — /api/sites/:siteId/staff - `GET /api/sites/:siteId/staff` — List staff members - `POST /api/sites/:siteId/staff` — Invite/create staff - `PATCH /api/sites/:siteId/staff/:id` — Update staff - `DELETE /api/sites/:siteId/staff/:id` — Remove staff - `GET /api/sites/:siteId/staff/:id/availability` — Per-staff weekly availability ### Bookings — /api/sites/:siteId/bookings - `GET /api/sites/:siteId/bookings` — List bookings; query: `from`, `to` (ISO), `status`, `staffId`, `serviceId`, `customerId` - `POST /api/sites/:siteId/bookings` — Create booking (practitioner) or via public booking flow - `GET /api/sites/:siteId/bookings/:id` — Booking detail - `PATCH /api/sites/:siteId/bookings/:id` — Update booking (reschedule, change service) - `DELETE /api/sites/:siteId/bookings/:id` — Cancel booking - `POST /api/sites/:siteId/bookings/:id/confirm` — Confirm a tentative booking - `POST /api/sites/:siteId/bookings/:id/complete` — Mark completed - `GET /api/sites/:siteId/bookings.csv` — CSV export - `GET /api/sites/:siteId/bookings.ics` — iCal feed #### BookingDTO ```typescript interface BookingDTO { id: string siteId: string serviceId: string serviceName: string price?: number | null // øre customerName: string customerEmail: string customerPhone: string | null start: string // ISO 8601 UTC end: string // ISO 8601 UTC serviceDuration: number // minutes bufferAfter: number // minutes status: 'CONFIRMED' | 'CANCELLED' | 'COMPLETED' cancellationFeeDue: number | null // øre cancelledAt: string | null createdAt: string paymentStatus?: 'PENDING' | 'PAID' | 'REFUNDED' | 'FAILED' | null paymentMethod?: 'STRIPE' | 'MOBILEPAY' | 'BANK' | 'CASH' | null staffId: string | null notes: string | null } ``` #### Conflict handling Double-booking is prevented at the database layer via a partial unique index on `(siteId, start, staffId) WHERE status = 'CONFIRMED'` with `NULLS NOT DISTINCT`. Conflicting requests return HTTP 409. ### Availability — /api/sites/:siteId/availability - `GET /api/sites/:siteId/availability` — Query: `serviceId`, `staffId?`, `from`, `to` → array of available slots respecting business hours, closures, existing bookings, and per-service duration + buffer ### Customers — /api/customers Scoped by siteId via query or path. - `GET /api/customers` — Search; query: `siteId`, `q`, `limit`, `offset` - `POST /api/customers` — Create customer - `GET /api/customers/:id` — Customer detail (includes booking history) - `PATCH /api/customers/:id` — Update - `DELETE /api/customers/:id` — Delete (GDPR-safe — anonymises booking PII) ### Payments — /api/payments - `POST /api/payments/stripe/checkout` — Create Stripe Checkout session for booking or subscription - `POST /api/payments/stripe/webhook` — Stripe webhook receiver (signature-verified, do not call directly) - `GET /api/payments/methods` — List enabled payment methods per site - `POST /api/payments/mobilepay/initiate` — MobilePay payment flow (where enabled) - `POST /api/payments/refund` — Refund a paid booking (admin) ### Billing / subscriptions — /api/billing - `GET /api/billing/subscription` — Current subscription state - `POST /api/billing/portal` — Stripe customer portal session - `POST /api/billing/upgrade` — Upgrade to Pro / Business - `POST /api/billing/cancel` — Cancel at period end ### GDPR — /api/gdpr - `POST /api/gdpr/request-access` — Body: `{ email }` → emails a time-limited magic-link to the address on file - `GET /api/gdpr/verify?token=...` — Verifies token and returns a short-lived session for self-service - `GET /api/gdpr/export` — Authenticated GDPR data export (JSON) - `POST /api/gdpr/delete` — Request full data deletion > **GDPR security:** Never allow unauthenticated access to personal data via `?email=` alone. Always require a magic-link or verification code. Time-limited tokens (15–30 min), single-use, rate-limited, audit-logged. ### FAQ — /api/sites/:siteId/faq - `GET /api/sites/:siteId/faq` — List FAQ entries - `POST /api/sites/:siteId/faq` — Create FAQ entry - `PATCH /api/sites/:siteId/faq/:id` — Update - `DELETE /api/sites/:siteId/faq/:id` — Delete ### Waitlist — /api/waitlist - `POST /api/waitlist` — Body: `{ email, plan?: 'pro' | 'business' }` → join waitlist - `GET /api/waitlist/admin` — Admin list (auth required) ### Public booking flow (no auth) - `GET /api/public/sites/:siteId` — Public site profile - `GET /api/public/sites/:siteId/services` — Public services list - `GET /api/public/sites/:siteId/availability` — Public availability - `POST /api/public/sites/:siteId/bookings` — Create booking as end-customer (rate-limited) ## Webhooks (inbound) - `POST /api/payments/stripe/webhook` — Stripe events: `checkout.session.completed`, `payment_intent.succeeded`, `payment_intent.payment_failed`, `charge.refunded`. Signature verified with `STRIPE_WEBHOOK_SECRET`. - `POST /api/email/inbound` — Cura Hub inbound email (SES → Lambda → S3 → API). Used for booking-related email replies and support. ## Webhooks (outbound) Per-site configuration allows POSTing booking events to a customer-supplied URL. Event types: `booking.created`, `booking.updated`, `booking.cancelled`, `booking.completed`, `payment.succeeded`, `payment.refunded`. Payload: ```json { "event": "booking.created", "timestamp": "2026-05-19T07:30:00Z", "siteId": "studio-abc", "data": { /* BookingDTO */ } } ``` Signed with HMAC-SHA256 of the request body using the site webhook secret, in the `X-Simple-Bookings-Signature` header. ## MCP Server Simple Bookings provides an official MCP (Model Context Protocol) server for AI agents: ``` npm install -g @simple-bookings/mcp ``` Tools available (subject to scope-based authorisation): - `sb_list_sites`, `sb_get_site` - `sb_list_services`, `sb_list_staff` - `sb_get_business_hours`, `sb_list_closures` - `sb_list_bookings`, `sb_get_booking`, `sb_create_booking`, `sb_update_booking`, `sb_cancel_booking` - `sb_get_availability` - `sb_search_customers`, `sb_get_customer` Authentication: per-agent API keys with scoped permissions. See https://www.npmjs.com/package/@simple-bookings/mcp. ## Versioning and stability The API is currently versioned by URL path (`/api/`) without an explicit version segment. Breaking changes are announced via the blog and email. Beta endpoints may be marked with `/api/_beta/` and are not subject to stability guarantees. ## Contact - Support: hej@simple-bookings.dk - Security: report vulnerabilities to security@simple-bookings.dk - Company: Beaconware ApS, CVR 25469879, Denmark