# SmartBook public API > Same-origin REST used by guest booking pages and the booking widget. OpenAPI: https://smartbook.now/openapi.json — HTML: https://smartbook.now/developer This document matches production as of 2026-09-21. It is not an MCP server. There is no directory API: you must already know an organization slug (sitemap: https://smartbook.now/sitemap.xml). ## Base URL `https://smartbook.now/api/v1` JSON request/response unless noted. Errors: `{ "error": string, "details"?: unknown }`. ## Authentication and CSRF Public GET catalog/availability: no auth. POST /bookings, POST /bookings/cancel, POST /payments/create-intent: 1. `Origin` or `Referer` must be `https://smartbook.now`, `https://www.smartbook.now`, or `https://{org}.smartbook.now`. Missing or foreign origin → 403 `Invalid origin`. 2. Cookie `csrf-token` must equal header `x-csrf-token`. GET any `/api` path first to receive the cookie. Mismatch → 403 `Invalid CSRF token`. POST /ai/chat is CSRF-exempt. `X-API-Key` is only for platform-issued partner keys. Partner catalog reads and bookings with `source: "api"` require Discovera integration enabled on that organization. Merchants cannot self-issue keys. Dashboard/org management uses a Supabase session. That surface is not documented here. ## Catalog ### GET /services?slug={slug} or `?orgId={uuid}` or `?serviceId={uuid}`. Lists active public services (`visibility=public`, `list_on_public_page=true`). Partner listings are omitted. Useful fields: `id`, `name`, `duration_minutes`, `price_nok` (NOK major units), `is_multi_day`, `confirmation_mode`, `capacity`. Extra columns may be present. `guest_instructions` is omitted for anonymous callers. Optional `locale=nb|en`. ### GET /packages?slug={slug} Active packages with items, `primaryServiceId`, `totalPriceNok`. POST to create packages is owner-session only (not public). ## Availability ### GET /availability?serviceId={uuid}&date={YYYY-MM-DD} Timeslot / duration services. Response: ``` { "data": [ { "startTime": "ISO-8601", "endTime": "ISO-8601", "available": true, "remaining": 2 } ], "timezone": "Europe/Oslo" } ``` `data` is an array of slot objects, not clock strings. Dates beyond the platform advance window → 400. ### GET /availability/range?serviceId={uuid}&checkIn={YYYY-MM-DD}&checkOut={YYYY-MM-DD} Overnight / multi-day only (`is_multi_day=true`). Optional `resourceId`. Returns `available`, `nights`, `startTime`, `endTime`, `totalPriceNok`, `availableResources`, `conflicts`. Non-stay services → 400. ## Bookings ### POST /bookings Rate limit: 30 / IP. Required body (camelCase): - `serviceId` (uuid) - `startTime` (ISO-8601 datetime, not later than one year) - `customerName` - `customerEmail` - `customerPhone` (digits and `+` only, 8–20 chars) Optional: `notes`, `guestCount`, `dateRangeStart`, `dateRangeEnd` (YYYY-MM-DD, stays), `resourceId`, `packageId`, `items` (`[{ serviceId, quantity }]`), `source` (`direct` | `chatbot` | `api` | `partner`), `bookerEmail` (required for `partner`). Snake_case `service_id` / `date` / `time` / `customer_name` is rejected. 201: ``` { "data": { "id", "status", "start_time", "end_time", "price_nok", "org_id", ... }, "manageToken": "..." } ``` Booking is typically unpaid (`payment_status` pending). Manual-confirmation services may be a request, not instant confirm. Keep `manageToken` for pay and cancel. `source: "api"` without a valid key → 401. Discovera off → 403. ### POST /bookings/cancel Rate limit: 10 / IP. Body: `{ "token": "" }`. Not `{ "booking_id" }`. 200: `{ "data": { "status": "cancelled", "refundAmount": number } }`. Invalid token → 401. Failed refund → 502 and the booking stays. ## Payments ### POST /payments/create-intent Rate limit: 15 / IP. Body: `{ "bookingId": "uuid" }` only. Amount and currency are computed on the server. Do not send `amount` / `currency` / `booking_id`. Auth: Supabase session whose email matches the booking, **or** header `x-management-token: `. Otherwise 401. Stripe only. Vipps → 403. Manual services cannot be paid before owner approval. Expired payment window → 400. 200 embedded: `{ "data": { "provider": "stripe", "kind": "embedded", "clientSecret": "..." } }`. There is no public hosted checkout URL in this response. The guest widget uses the client secret. ## AI ### POST /ai/chat CSRF-exempt. Rate limit: 30 / IP. Body: `{ "messages": [...], "slug": "org-slug" }`. Optional `businessName`, `locale`, `guestLanguage`. Field is `slug`, not `business_slug`. Streams a Vercel AI SDK data stream (`toDataStreamResponse`). This is the in-page booking chatbot, not OpenAI-compatible chat completions. ## What this API does not provide - Search / list of all businesses - MCP, A2A, or other agent protocols - Merchant self-serve API keys - Idempotency-Key - A payment link that works without CSRF + manage token + Stripe.js - Vipps ## Product pages - https://smartbook.now/ — platform - https://smartbook.now/signup — create a business - https://smartbook.now/{slug} — public site for one organization - https://smartbook.now/{slug}/book — booking entry - https://smartbook.now/manage?token= — guest manage (token from email / create response) - https://smartbook.now/contact - https://smartbook.now/terms — guest terms - https://smartbook.now/vilkar — business terms - https://smartbook.now/privacy