Inspect the contract. Build against generated truth.
This reference reads the same typed endpoint registry that generates Booking Bible’s OpenAPI 3.1 document. Start with a group, then open only the parameters and examples you need.
Route handlers are the complete deployed /api/v1 surface. Documented operations are the partner-facing contracts currently registered for OpenAPI.
Start with the contract
Authentication and versioning are explicit
Public discovery routes need no credentials. Protected routes accept a user bearer token, an organization-scoped API key, or the method documented for that operation.
Organization-scoped credentials
Create API keys under Admin → Settings → Developer. Each key is shown once, carries explicit scopes, and remains bound to its venue.
Version pinned by header
Send X-Api-Version to pin behavior. The current documented version is 2026-04-11.
Groups and operations below are generated from the application registry. Download the OpenAPI JSON for code generation or machine-readable inspection.
Discovery20 documented operations
GET/api/v1/discoveryPublic
Discover live venue inventory
Bounded, paginated Universe discovery for one canonical world and venue-local date. Returns capped live class or service/appointment summaries with explicit partial failures; member coordinates are not accepted.
Parameters, scopes and examples
Query parameters
worldstring · required
Required: classes, treatments, or salon
datestring · required
Required venue-local date (YYYY-MM-DD)
time_windowstring
any, morning, afternoon, or eveningDefault: any
searchstring
Venue, location, class, or service search
pageinteger
Page numberDefault: 1
limitinteger
Venue items per page (max 4)Default: 4
GET/api/v1/discovery/countsPublic
Live per-world "open today" venue counts
Lightweight counterpart to /api/v1/discovery for the Explore landing screen: how many venues actually have live same-day inventory in each world today, not how many venues merely support the world. Reuses the same same-day-activity signals as discovery ordering (classes: class_instances existence; treatments/salon: the union of provider_schedules rostering and real appointments existence — a same-day proxy, not exact slot availability).
Full venue profile including brands, locations with rooms, opening hours, amenities, and photos.
Parameters, scopes and examples
Path parameters
slugstring · required
Venue URL slug
GET/api/v1/venues/{slug}/joinBearer token
Check venue join eligibility
Bearer-authenticated, read-only membership check for Consumer apps. Resolves the target from its slug or organization UUID and reports member, can_join, or an unavailable reason without changing account state.
Parameters, scopes and examples
Path parameters
slugstring · required
Venue URL slug or organization UUID
POST/api/v1/venues/{slug}/joinBearer token
Join a venue with explicit consent
Bearer-authenticated and Idempotency-Key protected. Requires {consent:true}; creates one active member relationship without changing an existing role, assigns the venue client ID, and emits the canonical audit, analytics, and member.created integration events.
Parameters, scopes and examples
Path parameters
slugstring · required
Venue URL slug or organization UUID
Explicit user consent to add this venue to their account.
Request body
{
"consent": true
}
GET/api/v1/venues/{slug}/schedulePublic
Get class schedule
Live class schedule with real-time availability. Filter by date range, location, brand, class type, instructor, or online-only. Each row includes the additive general-policy `requires_workshop_entry` flag and a nullable public `workshop_entry_target`; `is_bookable` retains its capacity/status/time meaning.
Bearer-authenticated exact class detail for Consumer push deep links. Requires X-Organization-ID for an active member relationship, retains public/member-entitled completed or cancelled classes, and never exposes an unlisted class. Includes the same additive general-policy `requires_workshop_entry` and nullable public `workshop_entry_target` fields as the venue schedule; `is_bookable` remains capacity/status/time-only.
All active pass types with pricing tiers, binding commitments, class restrictions, and location availability. Returns an `{ org, pass_types }` envelope (BB-R4): `org` carries `slug`, `name`, `currency`, `timezone`, and `vat_exempt_age_threshold` (for an under-/over-threshold pricing toggle); each `pass_types` entry includes its `slug` for `/buy/{slug}` deep-links. Configurable recurring entries also include `pricing_mode`, billing cadence, the immutable active pricing version, quantity range/step, volume tiers, unlimited option, and change-cycle policy. Optional `category`, `location_id`, `brand_id` filters apply to `pass_types`.
Class catalog with descriptions, difficulty levels, durations, and included services.
Parameters, scopes and examples
Required scopes
read:classes
Path parameters
slugstring · required
Venue URL slug
GET/api/v1/venues/{slug}/instructorsPublic
Get instructors
Instructor profiles with bios, photos, and specialties.
Parameters, scopes and examples
Required scopes
read:instructors
Path parameters
slugstring · required
Venue URL slug
GET/api/v1/venues/{slug}/locationsPublic
Get locations
Physical locations with rooms, capacity, opening hours, amenities, and Google Maps integration.
Parameters, scopes and examples
Required scopes
read:locations
Path parameters
slugstring · required
Venue URL slug
GET/api/v1/venues/{slug}/brandsPublic
List brands
Active brands at this venue. Each entry includes identity (name, slug, description), theming (colors, logo, hero), social links, and a class_types_count for quick summary rendering.
Parameters, scopes and examples
Required scopes
read:brands
Path parameters
slugstring · required
Venue URL slug
GET/api/v1/venues/{slug}/brands/{brandSlug}Public
Get brand detail
Full brand record plus class_types tagged to this brand and the pass_types available for it (respecting pass_type_brands restrictions — passes with no brand-junction rows are venue-wide and are included).
Parameters, scopes and examples
Required scopes
read:brands
Path parameters
slugstring · required
Venue URL slug
brandSlugstring · required
Brand slug within the venue
GET/api/v1/geoPublic
Geo prefill for the signup form
Anon utility that reads Vercel's request-geo headers (`x-vercel-ip-country`/`x-vercel-ip-city`) so a client can prefill signup's optional `country`/`city` fields from the caller's own IP before submitting POST /api/v1/auth/signup. `country` is an ISO 3166-1 alpha-2 code; `city` is URI-decoded free text. Either is `null` when the header is absent (e.g. local dev). No DB touch; never cached (per-caller response).
PUBLIC (no auth) balance lookup for a venue gift card, for a storefront "check your balance" widget. Org resolved from {slug}; lookup scoped to that org's cards by the FULL generated code OR the printed physical barcode (same fallback `redeemGiftCard` uses). Returns the minimal `{ code, remaining_amount, currency, status, expires_at }` — never purchaser/recipient PII. Enumeration-hardened: an unknown code, a cross-org code under the wrong slug, and a cancelled card all return the SAME generic 404. IP rate-limited (20/min).
Parameters, scopes and examples
Path parameters
slugstring · required
Venue URL slug
codestring · required
Full gift-card code or the printed physical barcode
PUBLIC (no auth) gift-card preview so a BRANDED storefront can show a recipient what they were gifted ("Alex sent you a 3-month membership") before prompting signup/redeem — instead of bouncing them to BB's /gift/redeem/[code] venue portal. Accepts the generated code OR the printed physical barcode. Returns `{ code, gift_type, sender_name, gift_description, pass_name, amount, currency, status, expires_at }` — the sender's display name + a human gift description only, NEVER recipient/purchaser contact info, the personal message, or the redeemer. Enumeration-hardened: unknown code, wrong slug, and a cancelled card all return the SAME generic 404. IP rate-limited (20/min). Redeem itself is member-authenticated (POST /api/v1/gift-cards/redeem).
Parameters, scopes and examples
Path parameters
slugstring · required
Venue URL slug
codestring · required
Full gift-card code or the printed physical barcode
Member-authenticated request-a-booking for a service that accepts inquiries (`accepts_inquiries: true` on the public services list). Free-text preferred time, not a real slot — the venue converts it to a real appointment once a time is agreed. `Idempotency-Key` is optional but MUST be a UUID when sent (400 `INVALID_IDEMPOTENCY_KEY` otherwise); it is the database ingest request id. Every 201 and every 503 `INQUIRY_RECONCILE_FAILED` returns an `Idempotency-Key` RESPONSE header (mirrored as `error.details.request_id` on the 503) carrying the identity the inquiry was accepted under — your key when you sent one, the server-generated UUID when you did not. Retry a 503 with that exact value as `Idempotency-Key`: it replays the accepted inquiry (no second row) and re-drives only what is still missing. Retrying without it mints a new identity and files a duplicate inquiry. Rate-limited 10/min. Errors: 503 INQUIRIES_DISABLED (kill switch off), 404 NOT_FOUND (venue), 422 SERVICE_NOT_ACCEPTING_INQUIRIES, 422 VALIDATION_FAILED, 409 IDEMPOTENCY_KEY_REUSE_MISMATCH (same key, different answers — nothing written), 500 SUBMIT_FAILED (`details.reason` passthrough).
Parameters, scopes and examples
Path parameters
slugstring · required
Venue URL slug
Inquiry details
Request body
{
"service_id": "uuid",
"preferred_time": "Tuesdays or Thursdays after 17:00",
"message": "Looking for a 90-minute deep tissue session.",
"contact_name": "Jane Doe",
"contact_email": "jane@example.com",
"contact_phone": "+4520123456"
}
Response example
{
"data": {
"id": "uuid"
},
"error": null
}
GET/api/v1/me/inquiriesBearer token
My booking inquiries
The caller's own booking inquiries across every venue, newest first. Status is mapped to plain language (never the raw form_submissions enum): 'Sent — waiting for the venue', 'The venue replied', or 'Closed'; archived/spam/deleted rows are never returned.
Exchange a Google or Apple identity token for a Booking Bible session. Existing email/password accounts are unchanged. New accounts require a venue (organization_id or organization_slug). Unclaimed imported emails are not auto-linked.
Exchange a device-bound mobile token_hash or a 6-digit email OTP for a session. Mobile token hashes require the callback request_id and device-held code_verifier. Rate-limited 5/10 min per IP.
Check whether an email already has an account before signup. Returns a state hint (absent | active | password_never_used | imported_unclaimed | unknown). Rate-limited 20/min per IP. Constant-time floor of 250ms to prevent enumeration.
Create a TOTP challenge for one of the caller's factors. Returns {id, expires_at}; pass the id as challenge_id to /auth/mfa/verify. Repeating mints a fresh challenge (intended resend). Rate-limited 5/min per user.
Verify a 6-digit TOTP code (enrollment confirmation or login challenge). Accepts {type: totp, factor_id, code}, {type: backup_code, code}, or the challenge-bound {challenge_id, code} (factor resolved from the preceding /mfa/challenge). Rate-limited 5/5 min per user.
Generate 10 single-use backup codes for MFA recovery. Previous unused codes are invalidated. Codes are shown once in plaintext — only hashes are stored.
Parameters, scopes and examples
Response example
{
"data": {
"backup_codes": [
"ABCD1234EF",
"..."
],
"warning": "Save these codes securely. They will not be shown again.",
"count": 10
}
}
POST/api/v1/auth/mfa/resetBearer or API key
Admin MFA reset
Admin-initiated MFA reset for a user. Unenrolls all factors and invalidates backup codes. Permission: admin.users.manage.
Exchange refresh token for new access and refresh tokens.
Parameters, scopes and examples
Refresh token
Request body
{
"refresh_token": "xxx"
}
POST/api/v1/auth/logoutBearer token
Log out current session
Revoke the refreshable Supabase session represented by the caller JWT. The access JWT remains valid until its encoded expiry.
Parameters, scopes and examples
Response example
{
"data": {
"ok": true
}
}
POST/api/v1/auth/password/forgotPublic
Forgot password
Send a single-use 6-digit password verification code. Honours venue branding when an org is identified and never reveals whether the email exists. No reset link is generated.
Authenticated password change that still requires a fresh single-use numeric code. Submit the code and new password together; current-password-only and session-only changes are rejected.
NAMASTE-GATES-01 — mint a one-time SSO handoff code (60s TTL, single-use, SHA-256 hashed at rest) bound to an allowlisted destination domain. The destination site exchanges it at /auth/handoff-exchange for a fresh session.
NAMASTE-GATES-01 — consume a one-time handoff code (atomic single-use) and receive a fresh Supabase session for the bound user. Same session shape as /auth/login.
TV-DEVICE-AUTH-01 — RFC 8628-style device authorization (mint side). An input-constrained device (TV) receives a 256-bit device_code (its poll credential) plus a short user_code (shown as XXXX-XXXX + QR). Both are SHA-256 hashed at rest, bound to one 10-minute expiry, single-use.
TV-DEVICE-AUTH-01 — a SIGNED-IN member submits the user_code shown on the TV (normalized: uppercase, dashes/spaces stripped). Binds the pending device code to the caller so the TV poll returns a session. Every failure (unknown / expired / attempts-capped) is the same generic 400 INVALID_CODE; per-code attempts<5 cap.
Parameters, scopes and examples
The short code shown on the TV
Request body
{
"user_code": "ABCD-EFGH"
}
Response example
{
"data": {
"approved": true
}
}
POST/api/v1/auth/device/tokenPublic
Poll TV device code for session
TV-DEVICE-AUTH-01 — the TV polls with its device_code (every `interval` seconds). 400 AUTHORIZATION_PENDING until approved; 400 EXPIRED_TOKEN / 403 ACCESS_DENIED / 400 INVALID_CODE are terminal. On approval the code is consumed atomically (single-use) and a fresh Supabase session is returned — same shape as /auth/login.
Venue-local YYYY-MM-DD start of a pass usage cycle (inclusive). Requires organization_id.
cycle_endstring
Venue-local YYYY-MM-DD end of a pass usage cycle (exclusive). Requires organization_id.
afterstring
Cursor for pagination
limitinteger
Items per pageDefault: 20
POST/api/v1/bookingsBearer or API key
Book a class
Create a booking. Validates pass eligibility, capacity, booking window, daily limits, and class restrictions. Supports idempotency via Idempotency-Key header.
Retrieve a single booking with class details, pass info, and check-in status.
Parameters, scopes and examples
Required scopes
read:bookings
Path parameters
idstring · required
Booking ID
POST/api/v1/bookings/buddyBearer token
Invite a client to the same class
Creates and emails a venue-branded invitation to an existing active client at the same venue. The inviter must already have a confirmed booking. The recipient books with their own pass or payment; no guest funding or inviter entitlement is used. Idempotency-Key supported.
Accepts a buddy invitation for the authenticated recipient and books the same class with that recipient’s own eligible pass or normal venue booking rules. The invited email/account and active venue membership must match. Idempotency-Key supported.
NAMASTE-GATES-01 / LIVE-PARTICIPATION-01 — entitlement-based live-watch path. Requires an active pass whose type grants online class access and covers the class type; finds or creates the caller’s attendance_type=online booking (idempotent, respects online_capacity, consumes a clip only for clip-based passes) and returns a signed Mux playback URL plus viewer-session telemetry token. First admission opens 10 minutes before start and closes exactly at start; an admitted viewer may recover through end + 5 minutes unless they explicitly leave after start.
Client-facing: exchange a valid QR token for a check-in on the caller's booking. Token must be active and not expired. Anti-replay: a booking can only transition to checked_in once. Every scan writes an audit_log entry regardless of outcome.
Returns the authenticated member’s frozen selection, current published recurring choices, pricing version, and any pending renewal change. Owner-scoped and available only when member changes are enabled by the venue.
Preview a venue-approved quantity/unlimited change for renewal 1–24, or schedule it with the exact unexpired quote fingerprint. The change takes effect only after the target renewal invoice is paid.
Cancels the authenticated member’s pending future allowance change without altering the current frozen entitlement.
Parameters, scopes and examples
Path parameters
idstring · required
Issued pass UUID
GET/api/v1/pass-types/{id}Public
Pass-type catalog detail
Pass-type catalog detail used by checkout — price, duration, benefits, binding tiers, eligible class types, configurable kind, and the immutable published Flexible pass configuration when enabled. Active public items need no authentication; a hidden exhausted-credit target requires member JWT authentication plus its clips_empty_offer_id capability.
Parameters, scopes and examples
Path parameters
idstring · required
Pass-type id
Query parameters
clips_empty_offer_idstring
Opaque clips-empty path UUID. Revalidated against the authenticated member’s exhausted, still-valid source pass before a hidden target is returned.
Initiate pass purchase. Returns a PaymentIntent or SetupIntent client_secret (`client_secret_type` identifies which) with the provider-frozen customer, ephemeral key, Connect account, merchant country, and regional revision. Customer credentials are paired and may both be null only for a supported generic-sheet/no-customer result. Optional binding_months must identify a current server-side tier and is priced by the same canonical resolver as checkout preview; unavailable tiers return 422 rather than falling back. Flexible recurring passes require selection_kind=quantity with quantity, or selection_kind=unlimited; Flexible class/time passes require selection_kind=option with option_id. A recurring Flexible pass accepts only a Flash Sale backed by one shared introductory amount, which applies regardless of the chosen allowance. Fixed passes retain promo codes, gift cards, and account credits.
MEMBER (JWT) redeem endpoint so a branded storefront can host the whole redeem flow on its own domain. Applies the gift `{ code }` — generated code OR printed physical barcode — to the caller's account: a custom-amount gift credits the balance (`{ type:"credit", amount, newBalance }`); a pass gift creates + activates a pass (`{ type:"pass", passId }`). Atomic SELECT FOR UPDATE claim — two concurrent calls can never both redeem. A logged-out recipient must sign up / log in first (that creates/links the BB member); this endpoint is member-only by design. Org from the caller's active membership (X-Organization-ID header or single membership). Errors: 401 UNAUTHORIZED, 403 NO_ORG / MODULE_DISABLED, 400 VALIDATION_ERROR, 404 INVALID_CODE, 409 ALREADY_REDEEMED / EXPIRED / NOT_AVAILABLE, 500 REDEEM_FAILED.
Parameters, scopes and examples
Gift card code to redeem onto the caller's account
Gift cards the caller purchased or received (buyer, redeemer, or addressed recipient email), scoped to the active org. Returns `{id, code, initial_amount, balance, currency, status, recipient_email, recipient_name, message, expires_at, created_at}` with status `active|redeemed|expired|void`. Degrades to an empty list when the gift_cards module is off or no org context resolves.
POST/api/v1/gift-cards/purchaseBearer token
Purchase gift card
Buy a gift card (custom amount or a gifted pass) for a recipient. Creates a Stripe one-time PaymentIntent and returns client_secret plus customer_id + ephemeral_key for the Stripe Payment Sheet. Gated on the gift_cards module. Idempotency-Key supported.
Parameters, scopes and examples
Gift card purchase
Request body
{
"gift_type": "custom_amount",
"organization_id": "uuid",
"amount": 500,
"sender_name": "Alex",
"recipient_name": "Jordan",
"recipient_email": "jordan@example.com",
"delivery_method": "email",
"personal_message": "Enjoy a class on me!"
}
Anonymous (or logged-in) native gift-card checkout — phase 1. Mints a Stripe PaymentIntent for a gift card and returns client_secret so the buyer can mount Stripe Elements in a modal. Two gift kinds (exactly one of the two fields): a CUSTOM-AMOUNT gift via `amount` (smallest currency unit, min 5000, max 5000000), or a PASS-BASED gift via `pass_type_id` (GIFT-PASS-API-01 — must be an active, giftable, non-intro pass type of this org; price is server-resolved via calculateGiftPrice, optional `duration_months` 1–120 prepays a recurring membership). The gift_cards row is created only on confirm, so an abandoned payment leaves no orphan. Anonymous callers must pass a Turnstile token. VAT is accounted at redemption (multi-purpose voucher) so vat_amount is 0. Gated on the gift_cards module. Rate-limited 10/min.
Parameters, scopes and examples
Gift card checkout — custom-amount example; for a pass gift replace `amount` with `pass_type_id` (+ optional `duration_months`)
Request body
{
"organization_slug": "hot-yoga-cph",
"amount": 50000,
"recipient_email": "jordan@example.com",
"recipient_name": "Jordan",
"sender_name": "Alex",
"message": "Enjoy a class on me!",
"giver_email": "alex@example.com"
}
Native gift-card checkout — phase 2. After the buyer confirms the PaymentIntent client-side, this creates the gift_cards row (custom-amount OR pass-based — the pass identity rides in PI metadata), delivers it to the recipient, and emails the giver a receipt. Idempotent on the PaymentIntent — a retry or the webhook backstop never double-creates a card. Returns the last 4 of the code, a masked recipient email, and gift_type. organization_slug is optional but recommended for direct-charge venues.
Pause (freeze) a pass for a date range. Owner-scoped. Enforces the pass-type pause rules (allow_pause, min/max duration, annual freeze budget) + binding gate, pauses Stripe billing, and bills any pause fee. Idempotency-Key supported.
Move a deferred (pending_activation) membership start to today or an earlier future date: re-anchors Stripe billing, charges the first membership payment, and activates the pass. Owner-scoped. Idempotency-Key supported; rate-limited 5/min. Returns payment_status succeeded | requires_action (confirm with client_secret; the invoice.paid path then activates) | pending. Errors: PASS_NOT_FOUND, FORBIDDEN, ALREADY_STARTED, IN_PROGRESS, INVALID_START_DATE, PAYMENT_FAILED, STRIPE_UNAVAILABLE.
Parameters, scopes and examples
Path parameters
idstring · required
Pass id
New start date (must be earlier than the current start)
Owner-scoped, read-only termination summary resolved from the venue self-cancel gate, binding period, termination policy, Stripe billing boundary and remaining class balance. Returns can_confirm=false with the blocking reason while a binding period is active.
Parameters, scopes and examples
Path parameters
idstring · required
Pass id
POST/api/v1/me/passes/{id}/terminateBearer token
Terminate a recurring membership
Confirmed owner-scoped membership termination. Enforces venue allow_member_cancel, minimum membership age, binding period, required reason and the configured termination boundary. Stripe synchronization is fail-closed and Idempotency-Key is supported.
Parameters, scopes and examples
Path parameters
idstring · required
Pass id
Explicit acknowledgement and optional/venue-required reason
Request body
{
"acknowledged": true,
"reason": "Moving away"
}
GET/api/v1/me/passes/{id}/extensionBearer token
Get pass extension quote
Return the authenticated member’s venue-scoped self-extension policy and live quote: proposed expiry, price/currency, configured duration, remaining extension allowance, clips, and a machine-readable unavailable_reason. Requires X-Organization-ID and fails closed on invalid venue configuration.
Revalidates the venue’s live self-extension policy and creates a durable operation before any processor call. Paid responses include PaymentSheet customer/ephemeral-key credentials in the exact frozen Stripe namespace; direct mode returns stripe_account_id. Free responses still return operation_id but do not mutate the pass. Requires X-Organization-ID and Idempotency-Key.
Authoritatively rechecks owner, tenant, venue policy, maximum count, hard end, frozen Stripe provenance and payment status under database locks. Paid success atomically records payment, fee, audit, pass, and operation; explicit post-charge conflicts are idempotently refunded. Nonterminal 202 statuses are finalizing or refund_pending and are safe to retry. The Stripe webhook shares this reconciler. Idempotency-Key is required.
Parameters, scopes and examples
Path parameters
idstring · required
Pass id
Durable operation reference plus PI reference for paid extensions
Request body
{
"operation_id": "uuid",
"payment_intent_id": "pi_xxx (omit when free)"
}
Accept a pending pass-share invitation by token. Verifies the caller’s email matches the invite recipient, then grants booking access by appending the caller to `passes.shared_with` (respecting `pass_types.max_sharers`) and converges the share into the `pass_shares` table. Emits `pass.share_accepted`. Member-JWT. Idempotency-Key supported.
Published VODs and class replays for the caller's venue. Visibility public + members only; pass_restricted items are accessible via /video-catalog/:id once the pass check passes. Signed Mux playback URLs valid for 2 hours.
Parameters, scopes and examples
Query parameters
pageinteger
Page numberDefault: 1
limitinteger
Items per page (max 50)Default: 20
categorystring
Filter by category (class_recording | tutorial | workshop)
The caller's required/pending intake forms for their active org. Each entry is annotated with whether the member already submitted (the pre-booking form gate's source of truth). Returns [] when the `forms` module is disabled.
Parameters, scopes and examples
Response example
{
"data": [
{
"id": "uuid",
"slug": "new-client-intake",
"name": "New Client Intake",
"description": "Tell us about your practice and any injuries.",
"required": true,
"submitted": false,
"submission_id": null,
"submitted_at": null
}
]
}
GET/api/v1/forms/{id}Bearer token
Get form schema
Render schema (fields, steps, submit label) plus the venue's configured `legal_basis` (`consent` | `contract` | `legitimate_interest` | `legal_obligation`) for a single published form. Use `legal_basis` to render the matching privacy notice and, for a `consent` form, to present its required consent checkbox as the gate it is — a consent-basis submission is refused unless that box was ticked. Scoped to the active org — forms in other orgs return 404.
Parameters, scopes and examples
Path parameters
idstring · required
Form UUID
Response example
{
"data": {
"id": "uuid",
"slug": "new-client-intake",
"name": "New Client Intake",
"description": "Tell us about your practice and any injuries.",
"required": true,
"legal_basis": "consent",
"schema": {
"version": 1,
"fields": [],
"steps": null,
"submit_label": "Submit"
},
"thank_you": {}
}
}
POST/api/v1/forms/{id}/submitBearer token
Submit a form
Submit `{ answers }` for a published form. Validates required fields + types, persists a submission stamped with the caller, and routes it into the unified inbox. `Idempotency-Key` is optional but MUST be a UUID when sent (400 `INVALID_IDEMPOTENCY_KEY` otherwise) — it is both the HTTP replay token and the database ingest request id. The same key with the same answers replays the original response; the same key with different answers writes nothing and returns 409 `IDEMPOTENCY_KEY_REUSE_MISMATCH`, so mint a new key whenever the answers change. Every 201 and every 503 `SUBMIT_RECONCILE_FAILED` returns an `Idempotency-Key` RESPONSE header (mirrored as `error.details.request_id` on the 503) carrying the identity the submission was accepted under — your key when you sent one, the server-generated UUID when you did not. Retry a 503 with that exact value as `Idempotency-Key`: it replays the accepted submission and re-drives only the missing delivery. Retrying without it mints a new identity and files a duplicate. 422 with `details.missing[]` on required-field failures; 422 `CONSENT_REQUIRED` when a consent-basis form was sent without its consent box ticked; 409 `FORM_CONSENT_MISCONFIGURED` when the form itself cannot lawfully collect.
The caller's referral status for their active org: code, referred-friend count, conversions, rewards earned, and an anonymized (first-name + last-initial) per-referral list. Returns an empty summary when the `referrals` module is disabled.
Active public venues ranked by explicit favourite, then canonical pass, class-booking, and appointment signals. Returns counts and last activity; caller identity is server-bound.
Idempotently removes only the authenticated member and requested venue pair.
Parameters, scopes and examples
Path parameters
organizationIdstring · required
Venue organization UUID
GET/api/v1/me/credits/balancesBearer token
My venue credit balances
Complete ledger-derived balances grouped by venue and currency. Each row carries `balance` (the venue ledger total), `available` (that total minus credit held by open checkout reservations, which is what checkout will actually spend) and `reserved`. Amounts are in major units. Consumer is account-wide; branded requests are fail-closed to x-organization-slug.
POST/api/v1/me/avatar/upload-urlBearer token
Create avatar upload ticket
Returns a caller-owned, MIME-bound storage path and two-hour signed upload URL for PNG, JPEG, or WebP up to 5 MB.
PATCH/api/v1/me/avatarBearer token
Finalize my avatar
Validates caller path ownership, metadata, size, and image magic bytes before deriving and saving the public URL.
DELETE/api/v1/me/avatarBearer token
Remove my avatar
Idempotently clears the profile reference and removes only the caller-owned canonical avatar object.
GET/api/v1/me/workspace-profileBearer token
My active venue operating profile
Server-authoritative Business-app profile for the active venue selected by X-Organization-ID. Returns booking_mode (classes, appointments, or both), business_type, resolved class/appointment operation gates, venue surface applicability, appointment access/counts, active_modules, and the resolved vertical_modules visibility map. business_type is informational and never used to infer booking_mode. Surface values are venue-level applicability; clients must still intersect them with the caller's effective permissions from GET /api/v1/me.
Current user profile with all active venue memberships and roles. Each membership carries `permissions: string[]` (the caller's OWN effective permission keys for that org — per-user overrides applied over role/capability defaults, resolved identically to requireApiPermissionWithDefaults) and `capabilities: string[]` (the membership capability set, surfaced for every membership). To bound per-request cost in this multi-tenant app, `permissions` is resolved for the ACTIVE org only (top-level `permissions_scope: "active_org"`; non-active memberships carry `[]`) — mobile refetches /me on org switch. Workspace ownership is server-projected as `is_individual`, `is_owned`, `is_workplace`, `is_relationship`, `is_selectable`, and an explicit `workspace_group` (`owned`, `works_at`, `member_venues`, or `relationships`). Accepted role-bearing employer memberships remain selectable in Business under “Works at”; member-only Network relationships do not. Business clients must only put selectable rows in their workspace picker. Gates UI on these instead of discovering denials via 403s. A PATCH /admin/permissions/user/{userId} is reflected within ≤60s (permission-cache TTL). Caller's own permissions only. See docs/api/ME_PERMISSIONS_CONTRACT.md.
POST/api/v1/me/active-organizationBearer token
Switch my active workspace
Authoritatively switches the caller to an active, selectable workspace. When the caller owns an individual professional venue, accepted role-bearing employer memberships remain selectable; only non-operational/member-only relationships return WORKSPACE_NOT_SELECTABLE. The response includes effective permissions for the selected workspace so native role gating is safe immediately.
Resolved feature-module map for the caller's active org (C07): `{ <module_key>: { enabled, source, tier?, settings? } }` — the same four-tier resolution (plan → group → venue → tenant) the admin sees at /admin/features. Also includes `professional_collaborations`, which reflects the platform-wide teacher-settlements rollout independently of the venue-to-venue `network` plan gate. Drives every <FeatureGate> in the branded mobile app. Multi-membership callers must send X-Organization-ID; without it the map resolves empty (all off).
GET/api/v1/me/minimalPublic
Minimal auth check
Cross-origin auth check for venue marketing sites. Returns { logged_in, first_name, venue_id, preferred_brand_id } — or logged_in=false when no session. CORS is gated by the venue/brand embed_allowed_origins allowlist; unknown origins get no CORS headers (treated as "not logged in" by the caller).
GET/api/v1/me/entitlementsBearer token
My entitlements
NAMASTE-GATES-01 — the caller's entitlement matrix for one venue: can_book_physical (any active pass with grants_in_person), can_watch_online (grants_online_class_access), online_only, bookable_class_type_ids ("all" when any usable pass is unrestricted), and an active_passes[] summary (slug, category, grants, validity, clips). The create_booking RPC enforces the same matrix atomically.
Create an account-local Stripe SetupIntent plus matching Customer/ephemeral-key credentials. Requires an Idempotency-Key header. The response freezes the server-owned venue country and exact Connect account for native Payment Sheet initialization.
Member self-service cannot detach saved cards. This endpoint returns PAYMENT_METHOD_REMOVAL_NOT_ALLOWED; add a replacement card or contact venue staff instead.
Promote a saved card to the Stripe customer default (invoice_settings.default_payment_method). Empty body, idempotent via Idempotency-Key. GET /me/payment-methods then returns is_default:true on the matching row (PAY-P3.1).
Parameters, scopes and examples
Path parameters
idstring · required
Stripe payment method ID
POST/api/v1/me/payment-sheet-initBearer token
Initialise Payment Sheet
Setup-only flow for Stripe Payment Sheet (PAY-P1.1). Requires an Idempotency-Key header. Returns customer_id, ephemeral_key, setup_intent_client_secret, and apple_merchant_id in the exact SetupIntent home account: connected only in direct mode, otherwise platform. Use when collecting a saved card before any purchase.
Register an Expo push notification token for iOS/Android/web. app_variant is required so member, branded-venue, and staff deliveries cannot cross application boundaries. Branded and business tokens also require a validated organization context.
Deactivate the authenticated user's token or device before logout. The token/device selector is sent in the JSON body.
Parameters, scopes and examples
At least one token or device_id is required
Request body
{
"device_id": "installation-uuid"
}
GET/api/v1/me/notificationsBearer token
Notification history
Cursor/page-paginated email, SMS, push, and in-app history. Rows include source-aware `data`, `read_at`, and `app_variant`; X-App-Variant filters app-specific inbox events, while X-Organization-Slug narrows branded clients to their venue.
Self-scoped read marker. Idempotency-Key is required; another user’s row returns 404.
Parameters, scopes and examples
Path parameters
idstring · required
Notification id
POST/api/v1/me/notifications/read-allBearer token
Mark notifications read
Marks all of the caller’s unread rows read. X-Organization-Slug narrows a branded client to its exact venue; otherwise Consumer marks its cross-venue inbox. Idempotency-Key is required.
Returns the canonical ten-category catalog with effective email/SMS/push defaults, frequency caps, and per-member quiet hours for the active/requested organization.
Upserts canonical category toggles/frequency caps and quiet hours. Unknown categories are rejected and every database failure is returned; Idempotency-Key is required.
GET/api/v1/me/paymentsBearer token
List my payments
Cursor-paginated receipt-bearing payment ledger for the caller. Pending and failed attempts are excluded; successful, refunded, partially-refunded and disputed originals remain available with their payment receipt.
Parameters, scopes and examples
Query parameters
limitnumber
Page size (default 20, max 100)
afterstring
Opaque cursor from a previous page
GET/api/v1/me/invoicesBearer token
List my invoices
Cursor-paginated list of the caller's member-visible client invoices. Drafts are excluded and every row includes an authenticated document_path for the print-ready HTML invoice.
Parameters, scopes and examples
Query parameters
limitnumber
Page size (default 20, max 100)
afterstring
Opaque cursor from a previous page
GET/api/v1/me/invoices/{id}Bearer token
Invoice detail
Owner-scoped detail for one issued client invoice, including line items and the totals breakdown (subtotal, discount, VAT, total, amount_paid).
Streams the receipt PDF (application/pdf) for one of the caller's payments — branded merchant header, line items, VAT breakdown, totals. Cached in storage after first render.
Emails the venue-branded receipt PDF for one of the caller's own payments to the address already on file for their account — the same document served by the PDF download. No recipient field exists; any caller-supplied recipient is ignored. Idempotency-Key is required; a retried key replays the cached result instead of re-sending.
Parameters, scopes and examples
Path parameters
paymentIdstring · required
Payment id
Empty body — the request is never read.
Request body
{}
GET/api/v1/me/invoices/{id}/documentBearer token
Invoice print document
Authenticated owner- and venue-scoped print-ready HTML for one member-visible invoice.
Parameters, scopes and examples
Path parameters
idstring · required
Invoice id
GET/api/v1/me/refundsBearer token
List member refund receipts
Owner-scoped successful refund operations, cursor-paginated and optionally restricted by the branded organization slug. Split-tender operations are returned once with a signed negative amount.
Parameters, scopes and examples
Query parameters
limitnumber
Page size (default 20, max 100)
afterstring
Opaque cursor from a previous page
GET/api/v1/me/refunds/{refundId}/pdfBearer token
Refund receipt PDF
Owner- and venue-scoped canonical refund receipt PDF. Non-final, sibling, cross-member and cross-venue refund ids return a uniform not-found response.
Parameters, scopes and examples
Path parameters
refundIdstring · required
Refund id
GET/api/v1/me/loyaltyBearer token
Loyalty balance + history
The caller's org-scoped loyalty point balance plus a recent per-event history slice. Full paginated history is on /api/v1/me/loyalty/points.
GET/api/v1/me/loyalty/pointsBearer token
Loyalty points history
Cursor-paginated per-event loyalty point ledger for the caller.
GET/api/v1/me/streakBearer token
Attendance streak
Current + longest attendance streak, freezes remaining, and at-risk flag.
GET/api/v1/me/rewardsBearer token
Redeemable rewards catalog
Active loyalty rewards for the caller's org with affordability (is_locked) computed against the caller's balance.
POST/api/v1/me/rewards/redeemBearer token
Redeem a reward
Redeem a loyalty reward. Idempotency-Key supported; audited.
Parameters, scopes and examples
Redemption
Request body
{
"reward_id": "uuid"
}
POST/api/v1/feedbackBearer token
Submit feedback & tip
Rate a class (1-5 stars), leave a comment (optionally `anonymous`), and optionally tip the instructor via Stripe. The tip carries its own `anonymous` flag. The tip block of the response returns `client_secret`, `customer_id`, `ephemeral_key`, and `stripe_account_id` (non-null only in DIRECT charge mode).
Self-scoped class/appointment review and tip eligibility. Organization, target, settings, MobilePay capability, and prompt decision are server-derived from the owned source. Reads are side-effect-free unless `claim_prompt=true` is explicitly supplied by a prompt-mode entry check.
Parameters, scopes and examples
Query parameters
source_typestring · required
class or appointment
source_idstring · required
Owned booking id (class) or appointment id
claim_promptboolean
Reserve an in-app prompt only when true
POST/api/v1/post-attendance/reviewsBearer token
Submit class or appointment review
Creates one source-aware review after server-authoritative attendance/settings checks. Idempotency-Key required. `professional_rating`, tags, recommendation, anonymity, moderation, recipient notification, analytics, and webhooks are venue-controlled.
POST/api/v1/tipsBearer token
Tip a professional (no review)
Create a class or appointment tip in major currency units (`amount: 20` means DKK 20). Organization, professional, currency, Stripe account, and available methods are server-derived. Customer + ephemeral key are optional: customerless PaymentSheet still supports adding a card. MobilePay is returned only for verified Danish/DKK/venue-capable configurations. Idempotency-Key required.
Authenticated tipper-only reconciliation after PaymentSheet/MobilePay/3DS returns. Retrieves the server-owned PaymentIntent in its frozen Stripe account namespace, validates amount/currency/metadata, and emits receipts only after Stripe reports succeeded. Idempotency-Key required.
Parameters, scopes and examples
Path parameters
idstring · required
Tip id
GET/api/v1/tips/{id}Bearer or API key
Tip status
Poll a tip's status after confirming its PaymentIntent (incl. MobilePay / 3DS redirect returns). Access: the tipper (JWT), an org admin/manager (JWT), or an org-scoped API key. Cross-user / cross-tenant reads return 404.
Submit an Art. 15/16/17/20/21/22 request (access, erasure, portability, rectification, objection, art22 review). 30-day SLA. For erasure, account access is disabled immediately and the response reports erasure_status=pending_fulfillment; a super-admin performs the guarded erasure cascade within the SLA, while the SLA cron only alerts. Statutory records may be anonymised and retained for their legal period. Idempotency-Key required.
Parameters, scopes and examples
DSR request
Request body
{
"kind": "access",
"details": "Please send all data you have on me."
}
Re-trigger the guardian verification email for the caller's outstanding parental-consent request (C06). Matched by the authenticated email — no enumeration. Rotates the token and refreshes the 7-day expiry on the existing pending row (never a duplicate request). Empty body; Idempotency-Key supported; throttled 3/min per IP + 5/hr per user.
GET/api/v1/me/consent-statusBearer token
Active consents
Latest consent record per consent_type for the authenticated user (TOS, marketing, cookies, AI, etc). is_active is fail-closed and true only when the grant is unwithdrawn and policy_version matches the server-canonical current_policy_version; stale grants return requires_reacceptance=true.
GET/api/v1/me/consentBearer token
Current native consent state + venue requirement
The venue's photo/video consent requirement (when organization_id is given) plus the caller's version-aware state for legal, marketing, analytics, and photo/community consent types. A stale policy version is inactive and requires reacceptance.
Parameters, scopes and examples
Query parameters
organization_idstring
Resolve the venue photo-consent requirement
POST/api/v1/me/consentBearer token
Capture native consent
Grant or withdraw one supported legal, marketing, analytics, or photo/community consent for the caller. Grant versions are resolved exclusively from the server-canonical active policy; legacy client policy_version values are accepted but ignored, and an unavailable policy returns 503 without writing. Delegates to the canonical consent record/withdraw pipeline (audit + webhook).
The caller's health-questionnaire completion timestamp (completed_at, null when never submitted). Pre-check for the mobile hot-yoga booking gate.
POST/api/v1/me/health-questionnaireBearer token
Submit health questionnaire (Art. 9)
Submit the spa/hot-yoga health questionnaire for the caller's active org. Runs the Art. 9 contraindication consent gate, inserts a health_questionnaires row (plaintext responses; encrypted at rest by cron), stamps profiles.health_questionnaire_completed_at so the booking gate clears, and writes audit_log/user_events. Requires an Idempotency-Key (a double submit replays). Org resolved via X-Organization-ID / active membership.
Read the caller's external calendar-feed state: { token, enabled, generatedAt }. token is the opaque secret embedded in the public .ics feed URL (null when no feed is provisioned).
Enable the caller's external calendar feed and return the token. Idempotent — an existing token is returned unchanged (never rotated); a new one is minted (256-bit, base64url) only when absent. Empty body. Audited (calendar_feed_token_generated).
Revoke the caller's calendar feed: clears the token and disables the feed (the public feed then 404s). Empty body. Audited (calendar_feed_token_revoked).
Parameters, scopes and examples
Response example
{
"data": {
"enabled": false
},
"error": null
}
GET/api/public/calendar-feed/{token}Public
Public calendar feed
UNAUTHENTICATED — the opaque token in the path IS the credential. Returns one user's bookings as JSON for an external calendar subscription: { bookings, cancellations, userId, generatedAt }. bookings are upcoming events for the next 90 days; cancellations are bookings cancelled in the last 7 days (so calendar apps emit STATUS:CANCELLED). 404s on an unknown or disabled token (indistinguishable). Scoped strictly to the token's single user — no other user's data. 60 req/min per token.
Buyer-facing canonical PricingBreakdown for a pass type — net/VAT split, registration fee, total today + recurring, localized policy terms, the start-date window, the required legal artifacts (with already_signed), and the buyer's saved signatures. NO charge. Member-JWT + stable x-organization-id (preferred) or legacy x-organization-slug, both membership-scoped. Query: pass_type_slug (required), start_date, binding_months, locale (en|da). A selected binding tier is validated and priced server-side; unavailable tiers return 422.
POST/api/v1/me/checkout/sign-artifactBearer token
Sign a purchase-time legal artifact
Records a waiver / ToS / privacy / contract acceptance with IP + user-agent + version + signature. Idempotent on (user, document, version); a stale version → 409 force-refetch; a minor (DOB < 18) → 409 + parental consent. Supports saved-signature reuse (saved_signature_id) honouring signature_kind. Linked contracts require pass_type_slug and may include start_date; the endpoint idempotently creates/adopts the exact current-version pre-purchase contract before signing.
GUEST-INVITE-01 — whether the caller's passes qualify them to host a guest at this class, the venue guest price, the standard single-class price to strike through (compare_at_price, display only), and any invitations they already have open for it. `reason` is plain-language copy safe to render verbatim when `eligible` is false.
Creates the invitation plus its pending guest seat (a GUEST-PAY-01 `pending_payment` booking that holds NO capacity until paid). `payer:'guest'` returns the link to share; `payer:'host'` additionally returns a Stripe Checkout URL (saved card, new card, or MobilePay). `return_base_url` must be an allowlisted host or it is ignored.
Withdraws an UNPAID invitation and releases its pending seat. A paid guest spot is a real booking — cancel it through the normal booking cancellation path so the venue's refund and fee rules apply (409 `ALREADY_PAID`).
GET/api/v1/guest-invites/{token}Public
Resolve a guest invitation (public)
GUEST-INVITE-01 — the invitation landing page a friend opens. Anonymous-allowed by design (the token is the capability); returns who invited them, the class, the price and the struck-through standard price, and nothing else about the host's account. `state` is `needs_account` for a signed-out visitor, `payable` once signed in, plus `already_paid` / `cancelled` / `expired` / `class_started` / `class_full`.
POST/api/v1/guest-invites/{token}/checkoutPublic
Pay a guest invitation without an account
GUEST-INVITE-01 — the Guest Visitor branch. ANONYMOUS-ALLOWED (the token is the capability): the invited friend pays without creating an account and receives a Stripe Checkout URL. Deliberately does NOT claim the seat, so no profile is created and `bookings.user_id` stays the host. Confirmation is still the verified-payment webhook. Trade-off the calling site MUST surface: with no login, only the host or the venue can cancel it afterwards. Refuses with 409 `ALREADY_CLAIMED` once someone has linked the invitation to an account.
GUEST-INVITE-01 — the class filled up before the invited friend accepted. ANONYMOUS-ALLOWED. An unpaid invitation never held a seat, so this is a normal outcome, not an error: the friend joins the waiting list and is NOT charged. If a spot opens, `reinviteWaitlistedGuests` sends a fresh payment link. Returns `{ position, already_on_waitlist }`.
The invited friend, now signed in, takes ownership of the guest seat and gets a Stripe Checkout URL. Claiming rebinds `bookings.user_id` to their profile (the host stays on `host_user_id`), which is what makes the spot appear in their own bookings and cancellable by them under the venue's ordinary cancellation rules. Capacity is still only taken by the verified-payment confirm RPC.
Returns the authenticated member’s appointments with the exact updated_at concurrency token required for cancellation. Supports upcoming/past direction, status, venue narrowing and cursor pagination.
Parameters, scopes and examples
Query parameters
directionstring
upcoming | pastDefault: upcoming
statusstring
Appointment status
organization_idstring
Optional venue UUID narrowing
POST/api/v1/appointmentsBearer token
Book my appointment
Creates a free, pass-covered, or pay-at-venue member appointment. Paid-at-booking appointments use the checkout endpoints below. When the venue payment mode is client_choice, omit payment_choice (or send online) to pay now — installed clients that do not send a choice stay on the paid checkout path. Send payment_choice=venue only for an unpaid create. X-Organization-ID and a stable Idempotency-Key are required; client communication follows the locked member-transactional policy rather than staff-selectable channels.
GET/api/v1/appointments/{id}Bearer token
Get my appointment
Returns one owned appointment, including its updated_at concurrency token. X-Organization-ID is required.
Parameters, scopes and examples
Path parameters
idstring · required
Appointment UUID
DELETE/api/v1/appointments/{id}Bearer token
Cancel my appointment
Atomically cancels one owned current appointment. Current clients send the exact rendered updated_at token; a stale token returns 409 STALE_TARGET and must be refreshed. Temporarily, an installed legacy member client may omit the body and the server binds its owned row snapshot to the same atomic CAS—there is no retry-without-CAS path. X-Organization-ID and a stable Idempotency-Key are required.
Read-only preview of the consequence of cancelling one owned appointment right now. The window and fee come from the service row (services.cancellation_window_hours / cancellation_fee_amount, defaults 24 / 0) — the exact pair the cancel RPC enforces — so the number shown matches the number charged. An appointment with a paid deposit or a linked payment is blocked with blocked_reason "refund_required" rather than previewing a self-service refund; a terminal appointment is blocked "not_cancellable". Honours the optional x-organization-slug tenant scope; an appointment outside the resolved scope, or belonging to another client, returns 404.
Parameters, scopes and examples
Path parameters
idstring · required
Appointment UUID
Response example
{
"data": {
"appointment_id": "uuid",
"can_cancel": true,
"blocked_reason": null,
"cancellation_window_hours": 24,
"is_late": true,
"will_charge": true,
"fee_amount": 250,
"currency": "DKK",
"refund_expected": false,
"message": "You are inside the venue’s cancellation window, so a late-cancellation fee applies."
}
}
GET/api/v1/appointments/quoteBearer token
Preview appointment payment policy
Returns the authenticated member’s server-authoritative concrete provider, effective service price, deposit, amount due at booking, remaining venue balance, payment timing, and payment_at_booking_mode (venue | online | client_choice). An `any` provider request resolves to one currently available provider before pricing. Optional payment_choice=online|venue is honoured only when the venue mode is client_choice; omitted choice defaults to online so older clients keep paying at booking. A configured deposit still requires the deposit online. Requires X-Organization-ID.
Parameters, scopes and examples
Query parameters
service_idstring · required
Service id
provider_idstring
Provider UUID or `any`; the response contains a concrete provider UUID
start_timestring · required
ISO appointment start
payment_choicestring
Optional online | venue. Omitted = pay now when the venue lets the client decide.
Claims a durable, tenant-bound operation before creating an account-pinned Stripe PaymentIntent. Returns PaymentSheet credentials. X-Organization-ID and a stable Idempotency-Key are required.
Parameters, scopes and examples
Exact live slot and provider selection. provider_id may be a concrete UUID or "any"; the server freezes one available provider and its effective price before payment.
Retrieves the exact account-scoped PaymentIntent, requires processor status succeeded, rechecks live policy, creates the appointment idempotently, and atomically links payment/accounting. Slot conflicts are compensated with an idempotent refund; 202 finalizing states are safe to retry. The Stripe webhook uses the same reconciler.
Atomically resolves all selected resources within the API-key venue, updates independent class/workshop allowance buckets, and reconciles active participants.
Add a paid-claim website applicant to a managed course roster
Trusted server-to-server bridge for venue application forms. Resolves the API-key tenant, the pass type's managed course, the applicant client/membership, and an optional localized track name; then creates or annotates an active roster enrollment and books its upcoming course sessions. Self-reported paid_deposit/paid_full values are retained as claims requiring reconciliation and never fabricate or overwrite BookingBible payment ledger state. API-key only (write:members), rate-limited, Idempotency-Key required.
Pay for a course enrollment (early-bird-aware price, or the deposit when required). Requires an Idempotency-Key header and returns a Stripe PaymentIntent client_secret + customer_id + ephemeral_key + stripe_account_id for the Payment Sheet. The enrollment is created `unpaid`; on `payment_intent.succeeded` it flips to paid/deposit_paid and its sessions are booked (deduped on the payment-intent id). Gated on membership + venue legal docs. Member-JWT. COURSE-SUITE — the body additionally accepts optional `plan` (payment-plan id), `purchaser_type` (`individual`|`company`), and `company` details (name/VAT/address) for VAT-by-purchaser + debtor invoicing. A supplied plan must exactly match a currently offered server-side plan; only an omitted property uses legacy/default behavior. The GET `/api/v1/courses/{id}` course detail additionally returns a `staff` array — `[{ role, name, title_label, photo_url, show_on_landing_page }]` — for the landing-page teaching team (COURSE-SUITE-02 multi-trainer). CV3-03 — the GET detail also returns `payment_plans` (`{ plans: [{ id, kind, installment_count? }], collection_method }`, the normalized plan OPTIONS this purchase route accepts as `plan`) and, for an authenticated Bearer caller with an enrollment, `viewer_enrollment` (`{ id, enrollment_status, payment_status, payment_plan, amount_paid, total_amount, balance, installments: [{ installment_number, amount, due_date, status }] }`; the response is always `Cache-Control: private, no-store`).
Revenue, bookings, attendance, 30-day active clients, and average revenue per client — per brand for the given period (default last 30 days). Uses bookings.brand_id and payments.brand_id populated by HYC_2. Returns venue-wide (unbranded) totals alongside the brand rows.
Parameters, scopes and examples
Required scopes
read:reports
GET/api/v1/admin/dashboard/todayBearer or API key
Today at a glance
Today's class timeline with booking counts, check-in status, and room assignments.
Parameters, scopes and examples
Required scopes
read:schedule
GET/api/v1/admin/scheduleBearer or API key
Admin schedule
Full schedule view with internal data: per-status booking counts, notes, cancellation reasons, updated_at concurrency tokens, and fail-closed historical capabilities. include_historical=true requires scheduling.manage_history.
Parameters, scopes and examples
Required scopes
read:schedule
Query parameters
start_datestring
Inclusive ISO date/time lower bound
end_datestring
Inclusive ISO date/time upper bound
include_historicalstring
Include protected historical class rows; requires scheduling.manage_historyDefault: false
GET/api/v1/admin/appointmentsBearer token
Venue appointment schedule
Business-app venue-wide appointment list with updated_at concurrency tokens and authoritative, fail-closed historical capabilities. Filters by ISO window, direction, status, provider, and location. Permission: bookings.manage.
Parameters, scopes and examples
Query parameters
fromstring
Inclusive ISO start time
tostring
Exclusive ISO end time
directionstring
upcoming | pastDefault: upcoming
statusstring
Appointment status
provider_idstring
Provider UUID
location_idstring
Location UUID
limitnumber
Maximum 200Default: 100
POST/api/v1/admin/appointmentsBearer token
Create an appointment for a client
Creates a tenant-bound current/future appointment for a known member or contact-complete guest through the canonical atomic appointment engine. Venue-local past dates and historical attestation fields fail closed until the dedicated executor is installed. Permission: bookings.manage. Idempotency-Key required. Client delivery is default-silent: only an explicit notification_channels selection of email, sms, and/or push can send. A non-empty selection requires notifications.send and an authoritative availability preflight before the mutation; an unavailable channel fails without creating the appointment. Omitted or empty channels and legacy notify booleans remain silent.
Tenant-bound client, provider, service, location, payment, notes, lifecycle state, updated_at concurrency token, and authoritative fail-closed historical capabilities for the Business app. Permission: bookings.manage.
Parameters, scopes and examples
Path parameters
idstring · required
Appointment UUID
PATCH/api/v1/admin/appointments/{id}Bearer token
Operate an appointment
Atomic ordinary check-in, start, complete, no-show, cancel, or reschedule with the exact expected_updated_at token returned by GET. A stale token returns 409 STALE_TARGET without mutation. Each action is checked against its canonical permission. Past/terminal appointments, past reschedule targets, and historical attestation fields fail closed until the dedicated executor is installed. Idempotency-Key required. No-show, cancel, and reschedule are default-silent and accept an explicit notification_channels selection of email, sms, and/or push; a non-empty selection requires notifications.send and an authoritative availability preflight before mutation. An unavailable channel fails without changing the appointment. Omitted or empty channels and legacy notify booleans remain silent. Check-in, start, and complete are non-client-contact actions and reject notification_channels.
Parameters, scopes and examples
Path parameters
idstring · required
Appointment UUID
Discriminated appointment action with the opaque updated_at token returned by the appointment detail
Dedicated, atomic appointment-history command contract. Requires a UUID Idempotency-Key, appointments.manage_history plus the ordinary operation permission, REWRITE attestation, and a past effective_at. Existing-row operations require the exact expected_updated_at and expected_status returned by the appointment detail; retrocreate accepts only the closed non-financial appointment intent. Corrections are always silent and reject notification controls. The route returns 503 HISTORICAL_EXECUTOR_UNAVAILABLE without table-call fallback until the separately reviewed correct_appointment_historical database RPC is installed.
Parameters, scopes and examples
Required scopes
appointments.manage_history
Path parameters
idstring · required
Appointment UUID
Closed appointment correction; history_confirmation_token must be REWRITE
Tenant-bound class and appointment reviews for the Business app. Supports source, visibility, rating, and pagination filters. Anonymous reviewer identity is never returned. Permission: feedback.view.
Returns the consent-, preference-, contact-, and suppression-aware email/SMS reach for a venue-scoped marketing audience. Permission: marketing.flash_sales.
Returns canonical refundable headroom, payer receipt contacts, SMS availability with an explicit disabled reason, original card brand/last4, venue refund destinations, and eligibility. Native clients must use this response instead of deriving refund options locally.
Creates a venue-scoped promo and public offer, optionally dispatching a consent-gated email/SMS campaign. Idempotency-Key required. Permission: marketing.flash_sales.
Explains current automatic review-request rules and returns anonymized recent delivery outcomes. Permission: marketing.reviews.
GET/api/v1/admin/gift-cardsBearer token
List venue gift cards
Returns the venue gift-card ledger for the Business app, including remaining balance. Optional code query looks up one card. Module gift_cards. Permission: gift_cards.sell.
Parameters, scopes and examples
Required scopes
gift_cards.sell
Query parameters
codestring
Exact gift-card code for a balance lookup
POST/api/v1/admin/gift-cardsBearer token
Issue a desk gift card
Creates a venue gift card. Delivery is default-silent: omitted send_notification never delivers. send_notification=true requires notifications.send plus a recipient. Idempotency-Key required. Permission: gift_cards.sell.
Delivery is default-silent. send_notification=true sends the inbox reply and requires notifications.send; otherwise the body is stored as an internal staff note. Idempotency-Key required. Permission: bookings.manage.
{
"body": "We can do Thursday at 10.",
"send_notification": false
}
GET/api/v1/admin/services/bundlesBearer token
List combo packages
Phone-useful combo-package list with price and item count. Full bundle builder stays on web admin. Permission: bookings.manage.
Parameters, scopes and examples
Required scopes
bookings.manage
GET/api/v1/admin/staffBearer token
List staff
Phone-useful staff directory: name, role, and contact. Payroll, commissions, and certifications stay on web admin. Permission: staff.view.
Parameters, scopes and examples
Required scopes
staff.view
GET/api/v1/admin/sites/statusBearer token
Website status snapshot
Returns the org-scoped venue website summary for the Business app: site identity, draft/published versions, preview URL, publish-readiness blockers/warnings, and connected custom-domain verification/SSL state. Permission: sites.view.
Parameters, scopes and examples
Required scopes
sites.view
POST/api/v1/admin/sites/publishBearer token
Publish the venue website
Publishes one tenant-bound website through the canonical publish core. Requires sites.publish plus a stable Idempotency-Key. Returns readiness blockers when the draft is not yet publishable; older mobile builds may ignore additive warnings.
Parameters, scopes and examples
Required scopes
sites.publish
Tenant-scoped website publish request
Request body
{
"site_id": "00000000-0000-4000-8000-000000000001",
"note": "Published after mobile review"
}
POST/api/v1/admin/sites/ai/chatBearer token
Talk to the website builder AI
Business-app SSE transport for the org-scoped website builder assistant. Requires sites.manage. Returns the mobile AI event vocabulary over Server-Sent Events and adds `site_patch` events so clients can refresh status mid-turn.
Parameters, scopes and examples
Required scopes
sites.manage
Tenant-bound site-builder message
Request body
{
"site_id": "00000000-0000-4000-8000-000000000001",
"conversation_id": null,
"message": "Make the homepage warmer and highlight workshops.",
"attachment_ids": [
"00000000-0000-4000-8000-000000000002"
]
}
POST/api/v1/admin/sites/attachmentsBearer token
Upload a website-builder attachment
Multipart upload for the Business website builder chat. Requires sites.manage. The file is stored through the shared AI attachment pipeline and later referenced by `attachment_ids` on the site chat route.
Parameters, scopes and examples
Required scopes
sites.manage
DELETE/api/v1/admin/sites/attachmentsBearer token
Delete a pending website-builder attachment
Deletes one tenant-owned, not-yet-bound website-builder attachment before it is sent in chat. Requires sites.manage.
Returns the frozen current allowance, published recurring choices, pricing version, and pending renewal change for an organization-owned issued pass. Permission: passes.manage.
Preview or schedule a client Flexible membership change
Previews or schedules a quantity/unlimited change for renewal 1–24 against an unexpired quote. Organization ownership is enforced and the new entitlement applies only after the target renewal invoice is paid. Permission: passes.manage.
Creates, replaces, or removes the one automatic clips-empty offer for a limited recurring pass or still-valid class pack. The target may be hidden from public catalogs. Permission: passes.manage.
Returns every active venue pass type for authenticated POS staff, including pass types intentionally hidden from public consumer catalogs. Permission: pos.access.
Business-app stream-control DTO for one tenant-bound class instance. Returns source options without provider live-stream ids, stream keys, RTMP URLs, SRT URLs, or playback URLs; action availability includes exact disabled reason codes. Visible to venue schedule/check-in readers and assigned staff roster readers. Source/go-live writes repeat module/settings gates; end remains available for safe live shutdown.
Sets or clears the occurrence-level stream source before the class goes live. Requires class.create, active streaming module/entitlement, a streamable class, an active RTMP/SRT source with an attached provider stream, tenant binding, lifecycle CAS status=scheduled, UUID Idempotency-Key, and no assigned-instructor owner-toggle block. The operation is silent; notification fields are rejected.
Starts a class stream from an existing class provider stream or an active selected RTMP/SRT source. Requires class.create, active streaming entitlement/module, streaming settings enabled, streamable class, venue-local class date, tenant binding, lifecycle CAS status=scheduled, provider-adapter enablement, UUID Idempotency-Key, and no assigned-instructor owner-toggle block. Provider enablement is compensated when the DB transition fails or loses a race unless the winner uses the same stream. The route does not mint new one-time provider streams or expose source credentials; it is operationally silent and rejects notification fields.
Ends a live class stream. Requires class.create, tenant binding, lifecycle CAS status=live, and UUID Idempotency-Key. The class completion commits before the shared-stream provider disable guard runs; the response reports provider_stop as disabled, skipped_shared, failed, or not_applicable. The route records streaming usage after the class completion commit. It is operationally silent and rejects notification fields.
Retries only the provider disable step after a class has already completed; it never re-completes the class or repeats lifecycle effects. Requires class.create, tenant binding, UUID Idempotency-Key, and an attached provider stream. The shared-stream guard is organization-scoped and no blocking occurrence identifier is returned. This route is operationally silent and rejects notification fields.
PHONE-PUBLISH-01: mints an ephemeral, class-scoped provider live stream plus a hashed single-use claim token, bound to one organization, one class occurrence, and the preparing user, with a short server-enforced TTL. Requires class.create OR the assigned instructor when the venue enables instructor go-live, active streaming module/entitlement, streamable class, venue-local class date, the phone_publisher_sessions kill switch, tenant binding, and a UUID Idempotency-Key. Returns non-secret session state and the one-time claim token; NEVER ingest URLs, stream keys, or provider resource ids. One active session per class; supersession refuses while another device is actively publishing with a fresh heartbeat.
PHONE-PUBLISH-01: atomically consumes the single-use claim token and returns short-lived RTMPS ingest material exactly once (Cache-Control: private, no-store). Only the session creator may claim. Deliberately NOT idempotency-cached — a duplicate claim returns CLAIM_ALREADY_USED and the recovery path is revoke + prepare a new session; the old secret is never re-displayed. No ingest material is ever stored server-side.
PHONE-PUBLISH-01: transitions the claimed session to publishing and returns the authoritative session/provider/class status snapshot. Never marks the class live — only the provider active webhook does. Owner-bound, tenant-bound, UUID Idempotency-Key required.
PHONE-PUBLISH-01: periodic liveness touch returning session state, provider connection status (ingest fields stripped), and class lifecycle status. When the provider confirms an active input and the class is still scheduled inside the phone-publisher window, the snapshot reconciles the class to live (CAS; the cron sweep is the backstop). Owner-bound and naturally idempotent, so no Idempotency-Key is required. A stale heartbeat makes a publishing session eligible for takeover by another authorized device.
PHONE-PUBLISH-01: ends the active phone publisher session, completes the class when this session took it live (usage recorded after the completion commit), and records a DURABLE provider-cleanup outcome — a failed teardown is surfaced in GET state and retried, never hidden. Requires class.create or the permitted assigned instructor, tenant binding, and a UUID Idempotency-Key.
PHONE-PUBLISH-01: revokes a session whose device was lost or reinstalled or should no longer publish. The provider stream is disabled and scheduled for deletion, killing any leaked ingest material; a fresh session must be prepared and claimed — the old secret is never re-displayed. Requires class.create or the permitted assigned instructor, tenant binding, and a UUID Idempotency-Key.
PHONE-PUBLISH-01: explicitly retries a failed or pending provider teardown for a terminal publisher session. Requires class.create or the permitted assigned instructor, tenant binding, and a UUID Idempotency-Key.
Parameters, scopes and examples
Required scopes
class.create
Path parameters
classInstanceIdstring · required
Class instance UUID
PATCH/api/v1/admin/schedule/{id}Bearer or API key
Edit class instance
Update start/end time, instructor, class type, capacity, or room on a single class instance. Capacity can never go below the current booked count (422 CAPACITY_BELOW_BOOKED — cancel bookings first; no force override). Instructor/room changes are conflict-checked; instructor must be qualified. Notifications default silent: omitted controls and legacy notify_attendees never send. An explicit notify audience plus channel sends the branded schedule-change email/SMS/push to participants and/or instructors on time/instructor/room changes and requires notifications.send. Idempotency-Key honored; audit_log carries per-field from/to diffs.
Atomic, immutable-ledger correction for an isolated past class. Requires a UUID Idempotency-Key, scheduling.manage_history plus scheduling.manage, expected_updated_at and expected_status for an existing row, a past effective_at, and typed REWRITE attestation. Cancellation-state corrections additionally require class.cancel. Classes with roster, financial, course, workshop, import, streaming, or other linked records fail closed for specialist review. Notifications are always silent.
Parameters, scopes and examples
Required scopes
scheduling.manage_history
Path parameters
idstring · required
Class instance ID
Bounded historical class-instance correction
Request body
{
"operation": "class_instance.correct_timing",
"expected_updated_at": "2026-08-20T09:00:00.000Z",
"expected_status": "completed",
"history_reason": "Signed instructor log confirms the recorded class time",
"history_confirmation_token": "REWRITE",
"effective_at": "2026-08-20T10:00:00.000Z",
"intent": {
"startTime": "2026-08-20T08:00:00.000Z",
"endTime": "2026-08-20T09:00:00.000Z"
}
}
Atomic, immutable-ledger correction for a past recurring availability window. Requires a UUID Idempotency-Key, availability.manage_history, staff_portal.availability, staff.edit for another staff member, expected_updated_at plus expected_is_active for existing rows, a past effective_at, and typed REWRITE attestation. The staff path and tenant-owned row pin ownership. Notifications are always silent.
Applies explicit venue-local start/end time, room, instructor, capacity, or class type to 1–50 tenant-owned classes with per-row conflict/failure results. Notifications default silent; an explicit notify audience plus channel requires notifications.send and sends only after each successful write. Idempotency-Key required.
Cancels 1–50 tenant-owned classes through the canonical cancellation engine with per-row results. Notifications default silent: omission and legacy notify_attendees never send. Explicit notify audience plus channel requires notifications.send; clients support Email/SMS/Push and instructors support Email/SMS. Push without a selected client audience returns 422. Idempotency-Key required.
POST/api/v1/admin/schedule/{id}/cancelBearer or API key
Cancel class
Cancel a class instance (bookings cancelled, clips restored, audit + webhook). Notifications default silent: omission and legacy notify_attendees never send. Explicit notify.{audience,channels} requires notifications.send; channel overrides (email/sms/push) AND with client preferences. Clients support Email/SMS/Push; instructors support Email/SMS, and Push without a selected client audience returns 422. Mixed client+instructor Email/Push notifies clients by Email/Push and instructors by Email. Idempotency-Key honored.
Parameters, scopes and examples
Required scopes
write:schedule
Path parameters
idstring · required
Class instance ID
POST/api/v1/admin/schedule/{id}/substituteBearer or API key
Assign substitute
Replace instructor for a class. Validates no scheduling conflicts across locations. Notifications default silent. Explicit notify audience plus channel requires notifications.send; client delivery is Push-only and substitute-instructor delivery is Email-only; selecting both audiences with Email+Push sends those exact legs. SMS or a channel without its supported audience returns 422. Idempotency-Key honored.
Parameters, scopes and examples
Required scopes
write:schedule
Path parameters
idstring · required
Class instance ID
GET/api/v1/admin/checkinBearer or API key
Venue-local check-in day strip
The venue-local day's classes for the native staff check-in screen (Business app): per-class check-in/waitlist counts, room/instructor, and the day-navigation gates (today vs. read-only past/future). Defaults `date` to the venue-local today when omitted; optional `location_id` (query param or X-Location-ID header) narrows to one location.
Parameters, scopes and examples
Required scopes
read:bookings
Query parameters
datestring
Venue-local date, YYYY-MM-DD. Defaults to the venue-local today.
location_idstring
Restrict results to one location. Also accepted as the X-Location-ID header.
GET/api/v1/admin/checkin/{classInstanceId}Bearer or API key
Attendee list
Class roster with member details, pass info, native course_access covering this booking, add-ons, included services, check-in status, and class/booking updated_at concurrency tokens. Whole-class cancellations retain the preserved roster and each attendee’s previous status; ordinary client cancellations remain excluded.
Parameters, scopes and examples
Required scopes
read:bookings
Path parameters
classInstanceIdstring · required
Class instance ID
Query parameters
include_historical_recordsstring
Include protected cancelled/late-cancelled roster rows; requires scheduling.manage_historyDefault: false
Send a bulk email or SMS to a selected subset of one class instance. Submitted booking_ids are intersected server-side with the organization's active or whole-class-preserved roster; ordinary cancellations and stale/foreign ids are dropped and counted as skipped. Caller-supplied contact data is never accepted. Uses the canonical consent/suppression-aware bulk senders and requires the can_view_client_contact_info membership toggle. Idempotency-Key is honored.
Parameters, scopes and examples
Required scopes
members.contact
Path parameters
classInstanceIdstring · required
Class instance UUID
Channel, roster booking ids, and message
Request body
{
"channel": "email",
"booking_ids": [
"00000000-0000-4000-8000-0000000000b1"
],
"subject": "Class update",
"message": "Hi {{first_name}} — here is an update about your class."
}
POST/api/v1/admin/checkin/{classInstanceId}/{bookingId}Bearer or API key
Check in member
Check a member into class. Validates late arrival cutoff.
Parameters, scopes and examples
Required scopes
write:checkin
Path parameters
classInstanceIdstring · required
Class instance ID
bookingIdstring · required
Booking ID
POST/api/v1/admin/checkin/{classInstanceId}/{bookingId}/noshowBearer or API key
Mark no-show
Mark member as no-show. Applies no-show fee if configured.
Parameters, scopes and examples
Required scopes
write:checkin
Path parameters
classInstanceIdstring · required
Class instance ID
bookingIdstring · required
Booking ID
GET/api/v1/admin/membersBearer or API key
List members
Membership-driven client list with exact pre-pagination status and pass filtering. Native course grants count as covering entitlement: those clients are status=active (not no_pass) and each row may carry additive active_course_access { course_name } | null. Search by name, email, phone or venue client ID; optionally filter by tag, active pass type, or canonical pass family. pass_type_id and pass_family combine with AND semantics. Every successful response, including zero-match pages, includes meta.pass_type_options and meta.pass_family_options. Options expose distinct active-client counts across the full authenticated venue before pagination; pass types include current active templates plus archived templates still held by active clients, including types hidden from public pricing and course/workshop-managed types.
Parameters, scopes and examples
Required scopes
read:members
Query parameters
searchstring
Search by name, email, or phone
statusstring
Client status: active, inactive, new, or no_pass
tagstring
Filter by member tag
pass_type_idstring
Filter by active pass type
pass_familystring
Filter by pass family: recurring, class_pack, time_based, or intro_offer
Register an active client or send a pending client invitation. Enforces the venue plan limit, requires members.edit and an Idempotency-Key, and records a PII-safe audit event.
Full member profile: passes with course-fulfillment provenance, canonical native course_access, recent bookings/payments, tags, scores, credits, referrals, client_display_id, plus server-authoritative total_bookings and last_visit_at, per-channel notification_availability (email/SMS/push with exact unavailable reasons), contact_details_visibility {email,phone} (independent member.view_email / member.view_phone AND the membership contact toggle; contact_details_visible remains email AND phone for older app builds), and payments_visible. A 409 PROFILE_MERGED is returned when this profile was merged away in this venue, with primary_user_id of the survivor. Contact disclosure is fail-closed on PII_AUDIT_FAILED.
Soft-deactivates only the active membership at the selected venue; it never deletes the shared profile or changes memberships at other venues. Delivery is silent by default. An explicit notify object may select Email, SMS, and/or Push, which requires notifications.send and an availability preflight before the deactivation commits. A post-commit delivery failure is returned separately as notification_failure and never restores access. Protected Admin/Finance memberships retain their shared lifecycle authorization checks. Idempotency-Key required. Permission: members.delete.
Parameters, scopes and examples
Required scopes
members.delete
Path parameters
idstring · required
Member user ID
Deactivation reason and optional explicit client delivery channels
Tenant-scoped pass history with an exact total and opaque keyset cursor. Returns up to 100 records per page and never exposes processor subscription identifiers.
Deterministically merges live bookings and imported historical visits. Historical rows carry record_source=migration_history and read_only=true. The total is exact across both stores. Every row also carries source, payment_status, class_instance.local_date and a permission-agnostic actions block (cancel, remove_waitlist, change_pass, correct_attendance, check_in, mark_no_show — all false on an imported row). meta.venue_today is the venue-local date the today gates were measured against; meta.stats (first page only, absent when after is sent) holds exact counts over the entire filtered set.
Parameters, scopes and examples
Required scopes
members.view_insights
Path parameters
idstring · required
Active member ID
Query parameters
limitinteger
Items per page (1–100)Default: 25
afterstring
Opaque next_cursor from the previous page
pass_idstring
Only bookings funded by this pass
cycle_startstring
Venue-local YYYY-MM-DD start of a pass usage cycle (inclusive)
cycle_endstring
Venue-local YYYY-MM-DD end of a pass usage cycle (exclusive)
scopestring
upcoming = class start after now and the seat still held, ordered soonest first, no imported history; past = the exact complement, newest first. Omitted returns the merged view.
fromstring
Venue-local YYYY-MM-DD, inclusive, on the class start (imported rows compare on visit_date)
tostring
Venue-local YYYY-MM-DD, inclusive; converted to the next local midnight so evening classes stay in range
statusstring
Exact match on live rows; imported rows match on their normalised status, so confirmed, waitlisted and pending_payment never match one
class_type_idstring
Only classes of this class type (excludes imported history)
instructor_idstring
Only classes whose PRIMARY instructor is this person — a substitute does not match (excludes imported history)
location_idstring
Only classes at this location (excludes imported history)
brand_idstring
Only classes whose class type belongs to this brand (excludes imported history)
Tenant-scoped payments with exact total, refunds, safe card display, invoice linkage, and explicit receipt capabilities. Processor IDs, client secrets, and raw receipt URLs are never returned.
Parameters, scopes and examples
Required scopes
members.view_insights
Path parameters
idstring · required
Active member ID
Query parameters
limitinteger
Items per page (1–100)Default: 25
afterstring
Opaque next_cursor from the previous page
statusstring
Only payments with this status. Anything outside the list is a 400. Omitted = every status.
fromstring
Venue-local YYYY-MM-DD, inclusive. Applied to the page AND the total, so meta.total is the filtered total.
tostring
Venue-local YYYY-MM-DD, inclusive. from after to, or a day no calendar has, is a 400.
Re-sends through the canonical POS sender to the member contact stored on the server. Only same-venue POS-backed receipt payments are eligible; arbitrary recipients and payment retry are not supported.
Parameters, scopes and examples
Required scopes
notifications.send
Path parameters
idstring · required
Active member ID
paymentIdstring · required
Same-venue payment ID
Receipt delivery channel advertised by the payment receipt capability
Request body
{
"method": "email"
}
GET/api/v1/admin/paymentsBearer token
Org-wide recent sales
Business-app contract C6: the venue's recent payments (all statuses), newest first, mirroring the web sales drawer rows — plain-language method label, card label, money bucket (captured | recorded | internal), and the drawer's refund-offer rule (`refundable` = settled non-guest rows with money remaining). Amounts are integer minor units (øre). Cursor-paginated (opaque keyset cursor), limit ≤ 50. Range resolves in the venue's timezone.
Business-app contract C7: executes a claimed review through canonical processRefund. Body { review_intent_id, confirmation?, amount?, reason?, client_receipt_comment?, destination_id?, method_reference?, notify_client?, receipt_channels?: ('email'|'sms'|'push')[] }. Destination and notes must match the immutable review. Email is selectable (suppression is audited). SMS works only when review context reports available; otherwise execution rejects before money moves with 422 REFUND_SMS_UNAVAILABLE and unavailable_reason. Success includes refund_id, printable bearer-authenticated PDF URL, and per-channel outcomes.
Parameters, scopes and examples
Required scopes
billing.refunds.same_daybilling.refunds.full
Path parameters
idstring · required
Payment UUID
Refund details (amount in MAJOR units)
Request body
{
"review_intent_id": "00000000-0000-4000-8000-000000000000",
"amount": 199,
"reason": "Client requested the refund",
"client_receipt_comment": "We hope to see you again soon.",
"destination_id": "original",
"notify_client": true,
"receipt_channels": [
"email",
"push"
]
}
GET/api/v1/admin/refunds/{id}/receiptBearer token
Download canonical refund receipt PDF
Bearer-authenticated, tenant-scoped, no-store PDF used by native print/share. Contains only the optional client receipt comment; the staff-only internal reason is never rendered.
Business-app contract C8: send a bulk email or SMS to participants of one class instance. Recipients are resolved server-side — the submitted booking_ids are intersected with the class's ACTIVE roster (confirmed/waitlisted/checked_in); stale ids are dropped and counted as skipped, and caller-supplied contact info is never accepted. Delegates to the same senders/consent semantics as the web check-in bulk bar (templates admin_bulk_email / admin_bulk_sms). Requires the TV-D can_view_client_contact_info membership toggle. Returns { sent, skipped }.
Parameters, scopes and examples
Required scopes
members.contact
Path parameters
classInstanceIdstring · required
Class instance UUID
Channel, roster booking ids, and the message
Request body
{
"channel": "email",
"booking_ids": [
"00000000-0000-4000-8000-0000000000b1"
],
"subject": "Tonight’s class moves to Room 2",
"message": "Hi {{first_name}} — we moved tonight’s class to Room 2. See you there!"
}
Business-app parity: the eligible one-class products for paid guest spots. Uses the same guest visitor permission and catalog core as the web check-in screen.
Business-app parity: add 1–20 guest spots as payment-link, paid-at-desk, or comp/free bookings. Payment-link delivery supports email, SMS, or both. Uses the same context-free core as web; Idempotency-Key required.
Parameters, scopes and examples
Required scopes
booking.checkin
Path parameters
classInstanceIdstring · required
Class instance UUID
Guest contact, count, payment mode, product, and delivery channels
Business-app contract C1: the venue's failed payments (payments.status='failed') in the trailing window (default 30 days), newest first, cap 100. Each row carries client + linked pass context, a plain-language method label, and `retryable` per the same pure decider the web Retry button uses. Amounts are integer minor units (øre).
Parameters, scopes and examples
Required scopes
members.view_insights
Query parameters
daysinteger
Trailing window in days (1–365)Default: 30
member_idstring
Client-account parity P3 — only this client’s failed payments (public display id or UUID). `count` is then that client’s count. An id that is not an active client of this venue answers 404.
Business-app contract C2: re-collect a failed payment through the canonical retry core (PaymentIntent confirm or off-session invoice pay on the SC2-resolved Connect account, driving handleInvoicePaid). The body may be empty for the provider default, or contain payment_method_id selected from the exact failed invoice/PaymentIntent customer wallet. A provider-proven legacy card_ default is accepted and retried as the customer default without an unsupported override. Idempotency-Key is required, atomically claimed before the provider charge, and bound to the venue, payment, and selected payment_method_id; simultaneous reuse cannot double-charge and reuse with another card returns 409. Returns status succeeded | requires_action | failed with a plain-language message.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
paymentIdstring · required
Failed payment UUID
Optional exact saved card override; omit the body to use the provider default.
Record an external settlement for a failed renewal
Business-app contract C3: the failed recurring-renewal invoice was paid through another channel (cash, bank transfer, MobilePay, external card terminal, other). Settles the Stripe invoice out-of-band so the canonical recovery reactivates the pass, attributing the recovered payments row to the real method. amount is integer minor units (øre). Idempotency-Key required.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
paymentIdstring · required
Failed payment UUID
Settlement details
Request body
{
"method": "bank_transfer",
"amount": 79900,
"paid_at": "2026-07-31",
"note": "Paid via bank transfer, ref 1234",
"notify_client": true
}
Business-app contract C4: comp the failed recurring-renewal cycle — the client keeps the period, 0 revenue is recorded (the recovered payments row is forced to 'comped' amount 0). Reason required. Idempotency-Key required.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
paymentIdstring · required
Failed payment UUID
Waive details
Request body
{
"reason": "Goodwill — studio closure week",
"notify_client": true
}
Business-app contract C5: venue-imposed suspension (distinct from the member freeze) — blocks bookings until unsuspended. Client delivery is silent by default and accepts only notify.audience.clients=true plus an explicit Email/SMS/Push selection; legacy notify_client/notify_channels inputs remain silent. A non-empty selection requires notifications.send, and an unavailable selected channel returns PASS_NOTIFICATION_CHANNEL_UNAVAILABLE before the pass changes. Idempotency-Key (UUID) required; replay returns the stored response and the stable delivery reference prevents re-sending. Delivery failure after the pass write is reported as notification.sent=false and never rolls the suspension back.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Optional reason and explicit, default-silent client notification choice
Business-app contract C5: lift a venue-imposed suspension. Body is optional and silent by default. Client delivery requires notify.audience.clients=true, an explicit Email/SMS/Push selection, and notifications.send; legacy booleans/arrays remain silent. An unavailable selected channel returns PASS_NOTIFICATION_CHANNEL_UNAVAILABLE before the pass changes. Idempotency-Key (UUID) required; replay never re-sends. A post-write delivery failure returns notification.sent=false without rolling the pass change back. 422 NOT_SUSPENDED for a pass that is not suspended.
Business-app contract C5 (PASS-REACTIVATE-01): flip an expired/cancelled NON-recurring pass back to active; a run-out window requires new_end_date ≥ venue-local today. Client delivery is silent by default and requires notify.audience.clients=true, explicit Email/SMS/Push channels, and notifications.send; legacy notification inputs remain silent. Unavailable channels reject before mutation. Idempotency-Key (UUID) required and replay never re-sends; delivery failure after the write returns notification.sent=false without rollback. Recurring memberships are refused (422 RECURRING_UNSUPPORTED) — restart via a real re-mint. Audit pass_reactivated + reverse_payload.
Current attendance state for one booking plus what a correction would do: current_status, any attendance fee and its refund payment, the venue no-show fee, whether a clip was consumed, and per-channel notification_availability (email/SMS/push with the exact unavailable reason) so the client-notify picker can disable what cannot be delivered. can_refund_fee is always false here — refunds live in Billing.
Flip a booking between checked in, no-show, booked and removed for today or a past day (a future class returns 422 FUTURE_ATTENDANCE, a cancelled class 422 CLASS_CANCELLED). Marking a no-show additionally requires bookings.mark_no_show and removing a visit requires booking.cancel_member. Client delivery is silent by default: only an explicit notify object with a non-empty channel set sends, which requires notifications.send and a per-channel availability preflight before the correction (422 ATTENDANCE_NOTIFICATION_CHANNEL_UNAVAILABLE, 502 ATTENDANCE_NOTIFICATION_PREFLIGHT_FAILED). The legacy notify_client boolean is accepted but never delivers. refund_fee returns 409 REFUND_REVIEW_REQUIRED — refund the charged fee from its protected payment detail first. Idempotency-Key required.
Parameters, scopes and examples
Required scopes
booking.checkin
Path parameters
classInstanceIdstring · required
Class instance ID
bookingIdstring · required
Booking ID
Target attendance state, fee/clip choices, and explicit client delivery channels
The client's passes that could fund this booking, each with why it is or is not eligible (clips remaining, end date, who shared it, whether it is the current pass, and whether using it would shift the pass start date). Eligibility comes from the booking engine itself, so an option can never be offered that the engine would reject.
Move a confirmed, attended, no-show or waitlisted booking to another of the client’s passes; the clip movement is atomic. When the new pass has not started yet and this class is earlier, the change returns 409 PASS_SHIFT_CONFLICT with the classes that would be orphaned so staff can cancel them and retry. 422 PASS_REASSIGN_ERROR for any other refusal. Idempotency-Key required.
The venue catalogue the Business app builds the Visits filter sheet from: active class types (with their brand), this venue’s instructors, active locations and active brands. Served under the same grant as the list these choices filter, so a staff member who can see every booking can always load the sheet. Every list is org-scoped.
Removes one saved card from an active client of this venue. Requires members.contact and an Idempotency-Key (≤128 chars, bound to the venue, the operator, this client and this card; reuse for another card returns 409). The card must be on the client’s provider-proven wallet — an unknown id and another client’s id answer the same 404 PAYMENT_METHOD_NOT_FOUND, never an existence oracle. A shared platform-wallet card owned by another home venue answers 403 WALLET_FORBIDDEN. If the card was the default, the profile column and the exact Stripe customer default are cleared first (rolled back if the processor refuses) and only then is the card detached; a processor refusal answers 502 STRIPE_DETACH_FAILED. Audited; the client is never contacted.
Makes the named saved card the client’s default for future off-session charges. Requires members.contact and an Idempotency-Key. Only a card can be a default — a non-card method answers 422 NOT_A_CARD. 404 PAYMENT_METHOD_NOT_FOUND for an id not on the proven wallet, 403 WALLET_FORBIDDEN for another home venue’s shared wallet, 502 STRIPE_UPDATE_FAILED when the processor refuses (the local column write is rolled back). Setting the card that is already the default is a 200 no-op with no audit row. The client is never contacted.
Clears the client’s default card so nothing is charged off-session without a fresh choice. Requires members.contact and an Idempotency-Key. The path names the card the operator believes is current: if it is NOT the client’s default any more the request is refused with 409 NOT_DEFAULT (carrying the real default) rather than clearing a different card. 403 WALLET_FORBIDDEN and 502 STRIPE_UPDATE_FAILED as above. The client is never contacted.
Parameters, scopes and examples
Required scopes
members.contact
Path parameters
idstring · required
Active member ID
pmIdstring · required
The card the operator believes is the current default
Emails a payment receipt for each selected payment of this venue’s active client, including non-POS rows the single-payment receipt/send route skips. Requires members.contact plus notifications.send on this request, and an Idempotency-Key. Body is { payment_ids: uuid[] } (1…100). Each item reports sent, deduped, ineligible, or failed. The client is contacted by email only; omission of the key is 400, a foreign client is 404.
What the desk charge form needs before it can be shown: the venue currency, the display VAT rate (decimal) and its label, and the venue’s accounting categories. Requires pos.sell. An empty categories list means the venue has not set any up — the app disables the form and points at Settings rather than charging into a required column.
Charges an ad-hoc amount to the client’s saved card off-session (a phone payment, a fee) and records the sale. Requires pos.sell and an Idempotency-Key. amount_minor is an integer in minor units and is VAT-inclusive — exactly what the card is charged. The card’s proven Stripe customer/account is what the PaymentIntent is created on and confirmed on. 201 on success with the new payment id, the receipt reference and the card label. 422 CHARGE_NOT_CHARGEABLE with reason payments_disabled | not_on_wallet | below_minimum | connect_not_active means nothing was attempted and the same key may be reused. 402 CHARGE_REQUIRES_ACTION means the card needs the client’s own authentication — the app then offers the secure card link. 402 CHARGE_DECLINED and 500 CHARGE_FAILED keep the key. set_as_default applies the card as the default afterwards, best effort; default_updated is null when it was not requested. No receipt is sent — the app offers the receipt route afterwards with the returned payment_id.
Parameters, scopes and examples
Required scopes
pos.sell
Path parameters
idstring · required
Active member ID
The card, the VAT-inclusive amount in minor units, and the bookkeeping fields
The client’s open no-show and late-cancel debt, newest first. A declined charge stays on the list — pending and failed both mean money is still owed. Requires passes.manage, the same grant the web read uses; collecting or waiving needs billing.refunds.full. Amounts are integer minor units beside their currency, and each row carries the class name and start time when the linked booking still has them.
Collects an open no-show or late-cancel fee from the client’s card. Requires billing.refunds.full and an Idempotency-Key. The fee is loaded scoped to this venue (404 otherwise) and must still be open — a charged, waived or refunded fee answers 422 FEE_NOT_UNPAID with its status before anything is reserved. A declined card answers 422 FEE_CHARGE_DECLINED and the debt stays on the client. No card on file, or card payments not ready for this venue, answers 422 FEE_NOT_CHARGEABLE and nothing was attempted, so the same key may be reused. The client is never contacted.
Records that an open fee was collected outside the card processor — cash, bank transfer, MobilePay, an external card terminal, or other. Requires billing.refunds.full and an Idempotency-Key. There is no amount: a fee is always settled for its own amount. The venue-scoped load and the 422 FEE_NOT_UNPAID check run before anything is reserved. The underlying write is atomic and idempotent on a re-run. A refusal answers 422 FEE_SETTLE_REJECTED with the reason. The client is never contacted.
Parameters, scopes and examples
Required scopes
billing.refunds.full
Path parameters
feeIdstring · required
Cancellation fee ID
How the money was collected, and an optional internal note
Request body
{
"method": "bank_transfer",
"note": "Paid at the desk, ref 1234"
}
Forgives an open no-show or late-cancel fee: no money is collected and no revenue is recorded. Requires billing.refunds.full and an Idempotency-Key. The venue-scoped load and the 422 FEE_NOT_UNPAID check run before anything is reserved. reason is optional and is recorded in the audit trail only. The client is never contacted.
Parameters, scopes and examples
Required scopes
billing.refunds.full
Path parameters
feeIdstring · required
Cancellation fee ID
Optional internal reason recorded in the audit trail
The saved cards a retry of this exact failed payment may use, default first. The wallet is read live from the failed invoice or PaymentIntent’s own Stripe customer on the account that payment was made on, so the picker can never offer a card the retry would then fail to charge. Requires passes.manage. 400 INVALID_PAYMENT_ID for a malformed id, 404 NOT_FOUND for another venue’s payment, and 422 RETRY_OPTIONS_UNAVAILABLE when the payment is not failed, has no client wallet, or has no processor customer.
Mints a one-time, seven-day link the customer can use to pay back a refund that already completed and should not have. Authority is the same one the web action uses: a venue owner or finance staff member who also holds billing.refunds.full (403 FORBIDDEN otherwise). Idempotency-Key required. The refund is loaded scoped to this venue (404 otherwise) and must be succeeded; 422 REPAYMENT_LINK_UNAVAILABLE carries reason not_succeeded | already_repaid | activated_link_exists | attribution_review_required | payer_unresolved | no_email. Nothing is sent: the platform never contacts the customer here — the operator shares the returned link themselves, exactly as on the web.
The stat tiles and revenue breakdown for a client’s Billing tab: what they owe, how much of that is overdue, their account credit (which may be negative), their gift-card balance, their lifetime spend and their spend so far this venue-local month, plus their spend split by accounting category. Requires payments.view. Every amount is an integer in minor units. Lifetime and month-to-date are exact server-side sums, not a sample of recent rows; totals_truncated is true only for the rare client whose succeeded-payment history exceeds the 50,000-row walk, and the two sums are then the newest 50,000 payments rather than the exact figure. Saved cards are deliberately not included here — read them from the payment-methods route, which resolves the account the cards actually live on.
A client’s recurring product subscriptions (lockers, rentals), newest first, with the price in minor units, the billing interval, the next billing date and any scheduled cancellation date. actions.cancel is true only while the subscription is active, pending or past due — the same rule the web section applies. Requires members.view_insights. The app shows the section only when the list is non-empty.
Stops a recurring product subscription. mode period_end lets the client keep what they already paid for; mode now ends it immediately. Requires products.manage and an Idempotency-Key. The subscription is loaded bound to BOTH this venue and this client (404 otherwise) and must still be cancellable — anything else answers 422 SUBSCRIPTION_NOT_CANCELLABLE with its status before anything is reserved. A processor refusal answers 502 STRIPE_UPDATE_FAILED and the local row is unchanged. Cancelling cannot be undone. The client is never contacted.
Client-account parity P1 (A9): push a pass's validity end date out, running the same core the web pass card uses (audit pass_extended carries the previous end date, so the change stays manually reversible). Permission: passes.manage. Idempotency-Key required; a replay returns the stored response. Client delivery is silent by default and requires notify.audience.clients=true plus an explicit Email/SMS/Push selection AND notifications.send on the same request; an unavailable selected channel returns PASS_NOTIFICATION_CHANNEL_UNAVAILABLE before the pass changes. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'extend' } when the pass cannot be extended.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
New end date and the explicit, default-silent client notification choice
Client-account parity P1 (A8): add or remove clips on a clip card. A zero delta is refused with 422 INVALID_DELTA, and the resulting balance floors at 0 (the web rule — staff can zero a card, never owe it). Permission: passes.manage. Idempotency-Key required; a replay returns the stored response. Client delivery is silent by default and requires an explicit channel selection plus notifications.send. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'adjust_clips' } when the pass has no finite clip balance.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Signed clip delta, optional reason, and the client notification choice
Client-account parity P1 (A10): move the start and/or end of a pass's validity window — the "transfer the activation date" case for manually sold or mis-dated passes. Provide at least one of new_start_date, new_end_date or new_duration_days; an exact new_end_date always wins over a duration. 422 HARD_END_EXCEEDED when the new end is past the pass type's absolute end date, 422 INVALID_RANGE for every other rejected window, each with the plain-language message the app shows verbatim. The response reports already_expired when the resulting window ends before venue-local today (allowed — backdating a correction is legitimate). This never touches bookings. Permission: passes.manage. Idempotency-Key required. Client delivery is silent by default. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'adjust_dates' }.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
New window (dates and/or duration), optional reason, notification choice
Request body
{
"new_start_date": "2026-02-01",
"new_duration_days": 90,
"reason": "Sold in January, first class in February",
"notify": {
"audience": {
"clients": true
},
"channels": {
"email": true,
"sms": false,
"push": false
}
}
}
Client-account parity P1 (A11): flip auto_renew, syncing Stripe cancel_at_period_end on the subscription's OWN account (a connected-account subscription is always scoped to passes.stripe_account_id), then write the admin-attributed audit row pass.auto_renew_toggled_by_admin. Permission: passes.manage. Idempotency-Key required. Client delivery is silent by default and requires an explicit channel selection plus notifications.send. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'toggle_auto_renew' }.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Desired auto-renew state and the client notification choice
Client-account parity P1 (A21): the venue's non-recurring pass types in name order, minus this pass's current type and minus retired native-managed course/workshop passes — the exact list the web pass editor offers. Read-only: no Idempotency-Key, 30 requests / 60 s per operator. Permission: passes.manage. 404 for a pass that is not this venue's. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'convert_type' } for a recurring or already-ended pass.
Client-account parity P1 (A21): convert a NON-recurring pass (clip card / time pass) to another of the venue's non-recurring types. Credit-balance aware — the unused share of a price drop is issued as account credit and reported as credit_issued_major, so an upgrade never surprise-charges mid-pass. 422 TARGET_RECURRING when the source or the target is a recurring membership (convert those from the subscription page), 422 TARGET_NOT_FOUND for an unknown target or a retired course/workshop pass. Permission: passes.manage. Idempotency-Key required. Client delivery is silent by default. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'convert_type' }.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Target pass type and the client notification choice
Client-account parity P1 (A22): move a pass to another client of the SAME venue, with both-ends safety — the pass must belong to this venue and the recipient must hold an ACTIVE membership here (422 RECIPIENT_NOT_CLIENT); a recipient who already owns the pass is refused with 422 RECIPIENT_IS_OWNER, and a recurring membership with 422 RECURRING_UNSUPPORTED (manage those from the subscription actions). A reason is required and recorded on the audit row. Both ends are notified through the selected channels — the new owner AND the previous one — so BOTH clear the per-channel preflight before the write. Find recipient_id with GET /admin/members?search=. Permission: passes.manage. Idempotency-Key required. Client delivery is silent by default and requires notifications.send. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'transfer' } for anything but an active non-recurring pass.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Recipient profile id, required reason, and the client notification choice
Client-account parity P1 (A20): hide an ENDED pass from the client profile without rewriting its lifecycle — history is preserved, the profile is decluttered. Only a terminated, expired or cancelled pass may be archived (422 ARCHIVE_NOT_TERMINAL: end or terminate it first); a pass that is already archived is an idempotent success no-op. Permission: passes.manage. Idempotency-Key required. This route NEVER notifies the client (the web never does): a notify body is accepted and ignored, and notification_summary always reports no channels. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'archive' }.
Client-account parity P1 (A20): bring an archived pass back into the client profile. A pass that is not archived is an idempotent success no-op. Permission: passes.manage. Idempotency-Key required. This route NEVER notifies the client (the web never does): a notify body is accepted and ignored. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'unarchive' }.
Client-account parity P1 (A23): cancel a non-recurring pass immediately, with no refund — the pass editor's "Cancel now", server-side. Cancellation is TERMINAL by design: there is deliberately no undo, and the audit row carries everything needed to reconstruct state. Refunds are NOT part of this call — review the exact payment from the client's Billing tab. early_termination_fee reports the fee this cancellation determined applies (null once the binding period is over); this route never charges it. Permission: passes.manage. Idempotency-Key required. The branded cancellation notice is silent by default and requires an explicit channel selection plus notifications.send. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'cancel_now' }; 422 RECURRING_UNSUPPORTED if a recurring membership reaches the core (terminate those from the subscription actions).
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Optional reason and the explicit, default-silent client notification choice
Client-account parity P1 (A4): the authoritative Stripe-cycle preview behind the freeze panel. POST carries the two dates but the call is READ-ONLY — it changes nothing, so it takes NO Idempotency-Key and is rate-limited as a read (30 requests / 60 s per operator). preview is null when the pass has no Stripe subscription and there is nothing financial to review. The freeze itself recalculates inside the idempotent financial engine, so this review is advisory: invalidate it whenever either date changes, and echo preview.previewToken as expected_preview_token on POST /admin/members/{id}/membership/pause (422 PREVIEW_STALE when it no longer matches). Permission: passes.manage. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'freeze' } when the pass cannot be frozen.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Proposed freeze window (pause_end must be after pause_start)
Client-account parity P1 (A5): whether the pass's Stripe subscription is ACTUALLY paused, and whether that can be proven against the processor. Drives the "acknowledge unproven pause" confirmation on POST /admin/members/{id}/membership/resume, which accepts resume_from and acknowledge_unproven_pause. Read-only: no Idempotency-Key, 30 requests / 60 s per operator. A pass with no Stripe subscription answers hasStripePause=false, proven=true. Permission: passes.manage. 404 for a pass that is not this venue's; 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'resume' } when the pass is not in a resumable state.
Client-account parity P1 (A13): returns the pass's current renewal collection method. Capability gate: billing_mode (422 PASS_ACTION_NOT_ELIGIBLE otherwise). 404 NOT_FOUND for an unknown or other-venue pass.
Switch a membership between auto-charge and venue-collected
Client-account parity P1 (A13): choose how FUTURE renewals are collected. 'external' flips the Stripe subscription to collection_method='send_invoice' (Stripe stops auto-charging but keeps raising cycle invoices, and dunning skips the pass); 'auto_charge' restores automatic card collection. The Stripe update is scoped to the pass's own connected account. Capability: billing_mode. Idempotency-Key required. Client delivery is silent by default and accepts only notify.audience.clients=true plus an explicit Email/SMS/Push selection, preflighted on the membership_admin_changed event before the pass changes. Errors: 422 BILLING_MODE_UNCHANGED when the pass already uses that method, 422 NO_CLIENT, 422 STRIPE_NOT_CONFIGURED, 500 STRIPE_UPDATE_FAILED / UPDATE_FAILED. Audit: pass.billing_mode_changed.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Target billing mode and an explicit, default-silent client notification choice
Client-account parity P1 (A14, EXTERNAL-MINT-01): record that this cycle of an externally billed membership was collected at the venue. Settles the subscription's OLDEST OPEN Stripe invoice through the canonical rails — recordManualSettlement for the attribution intent, then paid_out_of_band — so the invoice.paid recovery writes the payments row, rolls the period, resets clips and sends the receipt. Scoped to the pass's own Stripe account. Capability: billing_mode, and the pass must be billing_mode='external' (422 NOT_EXTERNALLY_BILLED). Idempotency-Key required. Never notifies. Errors: 422 NO_OPEN_INVOICE, 422 NO_SUBSCRIPTION, 422 STRIPE_NOT_CONFIGURED, 502 SETTLEMENT_FAILED / OPERATION_FAILED. Audit: payment.settled_externally (irreversible).
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
How the venue collected the cycle
Request body
{
"method": "bank_transfer",
"note": "Paid at the desk, ref 4471"
}
Client-account parity P1 (A15): what is outstanding on a failed recurring renewal — amount, when it failed, how overdue it is, whether booking is suspended, and any pending late fee that can still be waived. Deliberately NOT capability-gated: a pass with nothing outstanding returns recovery=null so the card can hide itself, and 404 NOT_FOUND (unknown or other-venue pass) is the only refusal.
Client-account parity P1 (A15): settle the outstanding renewal off-session on the client's saved card, as the venue. Success rides the canonical invoice.paid finaliser, so the pass reactivates, the period rolls and the receipt sends. Capability: renewal_recovery. Idempotency-Key required; empty body. Never notifies directly. Errors: 402 RENEWAL_REQUIRES_ACTION when the card needs the CLIENT to confirm (the app then offers the payment link — the client secret is never on this wire), 422 RENEWAL_CHARGE_FAILED with the processor's plain-language reason.
Client-account parity P1 (A15): email and/or SMS the no-login pay link for the outstanding renewal (deduped once per pass, failure and day). The notification IS the mechanism, so notify is REQUIRED: notify.audience.clients=true plus at least one of channels.email / channels.sms, else 400 CHANNEL_REQUIRED. Push is ignored. notifications.send is re-checked on this request and each channel is preflighted on the renewal_payment_link event before anything is sent (422 PASS_NOTIFICATION_CHANNEL_UNAVAILABLE / 502 PASS_NOTIFICATION_PREFLIGHT_FAILED). Capability: renewal_recovery. Idempotency-Key required. 422 RENEWAL_PAYMENT_LINK_FAILED when the client has no reachable address or the send fails. Audit: membership.renewal_payment_link_sent.
Client-account parity P1 (A15): delete the late-fee invoice item dunning attached to this failure, while it is still pending. Once the fee lands on a finalized invoice there is nothing to delete and the venue resolves it through the normal refund path — that case answers 422 NO_PENDING_LATE_FEE, checked before the write. Capability: renewal_recovery. Idempotency-Key required; empty body. Never notifies. 422 WAIVE_LATE_FEE_FAILED when Stripe refuses. Audit: membership.late_fee_waived.
Client-account parity P1 (A19): delete a membership SETUP row that never became a membership — only when Stripe proves the subscription is dead and nothing links to it. Real history is never deleted. expected_updated_at is the optimistic-concurrency token (409 STALE when the row moved). The Idempotency-Key IS the cleanup operation key and MUST be a UUID (400 IDEMPOTENCY_KEY_INVALID); the atomic RPC records it, so a retry whose response was lost replays the committed removal instead of a false 404. Capability: cleanup_failed_setup. Never notifies. Errors: 404 NOT_FOUND, 422 HAS_HISTORY / HAS_LIFECYCLE_HISTORY / PROCESSOR_UNKNOWN / NOT_RECURRING / NOT_SAFE / NOT_REMOVABLE with the web's messages. Audit: membership.setup_artifact_removed, or membership.setup_artifact_removal_denied on a refusal.
Client-account parity P1 (A16): the whole Share-on-the-pass state for one pass — its shares (with the recipient's display name, monthly cap and classes used this month), its pending invites, and the sharer-slot budget from pass_types.max_sharers. Capability: share (a pass type that allows no sharers is 422 PASS_ACTION_NOT_ELIGIBLE). 404 NOT_FOUND for an unknown or other-venue pass.
Client-account parity P1 (A16): add ONE sharer on the owner's behalf. Provide exactly one of recipient_user_id (an existing client of this venue → an immediate pass_shares row) or email (a pending pass_share_invites row whose token the recipient redeems); both or neither is 400 VALIDATION_ERROR. An email that already belongs to a client of the venue shares immediately instead, and the response says which happened. The invite email IS the mechanism, so there is no notify body. Capability: share, and the pass must be active (422 PASS_NOT_ACTIVE). Idempotency-Key required. Errors: 422 NO_SLOTS, 422 ALREADY_SHARED, 422 RECIPIENT_NOT_CLIENT, 422 OWNER_RECIPIENT, 422 INVALID_EMAIL, 422 WRITE_FAILED. Audit: pass_share_created / pass_share_invited.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Exactly one of recipient_user_id or email, plus an optional monthly cap
Client-account parity P1 (A16): revoke an active share — the row goes status=revoked and the recipient drops out of passes.shared_with, which the booking RPC reads, so the shared capacity disappears immediately. Pass-scoped: a share on another pass or in another venue is the same 404 NOT_FOUND. Capability: share. Idempotency-Key required (no body; the share id rides the request fingerprint so a key reused for a different share is refused rather than replayed). Never notifies. Audit: pass_share_revoked.
Client-account parity P1 (A16): cancel a share invite before its token is redeemed, freeing the sharer slot it was holding. Only a still-pending invite can be cancelled — one already accepted or revoked answers 422 INVITE_NOT_PENDING. Pass-scoped: an invite on another pass or in another venue is the same 404 NOT_FOUND. Capability: share. Idempotency-Key required (no body; the invite id rides the request fingerprint). Never notifies. Audit: pass_share_invite_cancelled.
Accepts a Bearer JWT with `loyalty_price.grant` OR `passes.manage`. Unlike every other capability-gated pass read, a pass whose type offers no loyalty price and whose client holds no by-hand grant answers `{ context: null }` — never a 422.
Accepts a Bearer JWT with `loyalty_price.grant` (no API-key credential — this is a desk/termination-flow action). Requires an Idempotency-Key (≤128 chars). Issues the comeback offer with `origin: save_accepted` AND applies it immediately (`accepted_via: staff_save`), mirroring the web `acceptSaveOfferAtTermination`; notify is fixed silent. The app then continues its own termination flow.
Parameters, scopes and examples
Required scopes
loyalty_price.grant
Path parameters
idstring · required
Client id
The pass being saved and the win-back template to apply.
POST/api/v1/admin/loyalty-price/revokeBearer or API key
Take away a client’s by-hand loyalty price
Accepts exactly one credential: Bearer JWT with `loyalty_price.grant`, or an API key bound to the venue with `write:passes`. Requires a UUID `Idempotency-Key`, mirroring `loyalty-price/grant`. Writes through the context-free `revokeLoyaltyPriceCore` (the ONE writer).
Parameters, scopes and examples
Required scopes
write:passes
user_id + a short reason. Optional pass_id also clears a loyalty override on that pass.
Request body
{
"user_id": "uuid",
"reason": "Client asked to go back to the standard rate"
}
Accepts exactly one credential: Bearer JWT with `passes.manage`, or an API key bound to the venue with `write:passes`. Requires an Idempotency-Key (≤128 chars). Default-silent client notification — a non-empty channel selection requires `notifications.send` re-checked on the same request (JWT only; an API-key caller can never notify) plus a per-channel preflight before the mutation — refused as 422 `PASS_NOTIFICATION_CHANNEL_UNAVAILABLE` / 502 `PASS_NOTIFICATION_PREFLIGHT_FAILED`, the same codes every P1 pass route answers.
Parameters, scopes and examples
Required scopes
write:passes
pass_id + the agreed amount + a locked_until date (venue-local, must be after today) + a reason.
DELETE/api/v1/admin/rates/lock/{passId}Bearer or API key
Remove a pass rate lock
Accepts exactly one credential: Bearer JWT with `passes.manage`, or an API key bound to the venue with `write:passes`. Requires an Idempotency-Key (≤128 chars). Default-silent client notification — a non-empty channel selection requires `notifications.send` re-checked on the same request (JWT only; an API-key caller can never notify) plus a per-channel preflight before the mutation — refused as 422 `PASS_NOTIFICATION_CHANNEL_UNAVAILABLE` / 502 `PASS_NOTIFICATION_PREFLIGHT_FAILED`, the same codes every P1 pass route answers.
GET/api/v1/admin/rates/context/{passId}Bearer or API key
Get a pass’s full rate context
Accepts a Bearer JWT with `rates.view`, falling back to `passes.manage` on a 403 (mirrors the web `getPassRateContext`), or an API key bound to the venue with `read:passes`.
Client-account parity P4 (A27): the membership’s real billing history from the processor — the current cycle, whether it is set to cancel, the last twelve invoices with their paid/failed state and hosted links, the next payment, and the card on file. Requires passes.manage (not billing.manage — this mirrors the web editor’s own gate). Capability gate: subscription_timeline, so a non-recurring pass, an unfinished setup and a pass with no live subscription all answer 422 PASS_ACTION_NOT_ELIGIBLE. Every processor call is scoped to the pass’s own connected account. 502 STRIPE_ERROR when the processor is unreachable or unconfigured; 502 INCOMPLETE_PERIOD when it returns a billing period the app cannot render — retry. Nothing is written and the client is never contacted.
Client-account parity P4 (A29): shifts the membership’s next charge to a chosen date, with no proration — the canonical processor mechanism, scoped to the pass’s own connected account. Requires billing.manage, an Idempotency-Key and capability subscription_timeline. new_date is YYYY-MM-DD and must be in the future and within one year; anything else answers 422 INVALID_BILLING_DATE with the exact reason. 422 NO_STRIPE_SUBSCRIPTION when the membership has no recurring billing to shift. 502 STRIPE_ERROR frees the key for a corrected retry — the processor refused before anything moved. 500 DB_ERROR KEEPS the key, because the anchor already moved and a retry must not shift it twice.
Parameters, scopes and examples
Required scopes
billing.manage
Path parameters
passIdstring · required
Pass UUID
The new date, plus an optional explicit client-notify selection
Client-account parity P4 (A30): changes what the membership costs from its next payment onward. A new processor price is created on the SAME product and interval and swapped onto the subscription item with no proration; per-period overrides still win for the periods they cover. Requires billing.manage, an Idempotency-Key and capability subscription_timeline. amount_minor is an integer in the venue’s minor units and must be greater than zero. 422 NO_BILLABLE_ITEM when the subscription has no item to reprice. A 502 STRIPE_ERROR frees the key when the new price was never created, and KEEPS it when the price exists but the item swap failed — a retry must not create a second price. 500 DB_ERROR keeps the key (the processor already changed).
Parameters, scopes and examples
Required scopes
billing.manage
Path parameters
passIdstring · required
Pass UUID
The new recurring amount in minor units, plus an optional notify selection
Read a membership’s payment overrides and upcoming periods
Client-account parity P4 (A28): the SERVER-computed preview the web override panel renders, so the app never re-derives billing math. `preview` is the next `periods` billing windows (default 6, 1…36, anything else is 400 VALIDATION_ERROR) with each window marked as the base price or as a covering override, matched exactly as the invoice interceptor matches them. `overrides` is every saved row, ascending by start date; rows whose id appears in no preview period are the panel’s "Other overrides". A row with applied_payment_id set has already charged a real invoice and is locked (no edit, no delete). billing_active is false for a parked or migrated membership with no live subscription — overrides still save, they just stay staged until billing resumes, and billing_inactive_reason is the exact banner text. Requires billing.manage and capability subscription_overrides.
Parameters, scopes and examples
Required scopes
billing.manage
Path parameters
passIdstring · required
Pass UUID
Query parameters
periodsinteger
How many upcoming billing periods to preview (1…36)Default: 6
Client-account parity P4 (A28): sets an agreed price for upcoming membership payments. mode "periods" expands the membership’s real billing interval forward from its next payment date and writes one row per period; mode "range" writes one row covering an explicit window. amount_minor 0 is a comped period — nothing is charged. Requires billing.manage, an Idempotency-Key and capability subscription_overrides. 422 NO_UPCOMING_BILLING when "periods" is used on a membership with no next payment date; 400 INVALID_RANGE when the range ends before it starts; 500 DB_ERROR frees the key because nothing was written. `created` is how many rows were saved.
Parameters, scopes and examples
Required scopes
billing.manage
Path parameters
passIdstring · required
Pass UUID
Either the next N periods or one explicit window, plus an optional notify selection
Client-account parity P4 (A28): changes one not-yet-charged override’s amount and window in place. Requires billing.manage, an Idempotency-Key (bound to this pass AND this override, so a key cannot be replayed against another row) and capability subscription_overrides. The override is resolved only with both the venue and THIS pass, so an unknown id, another venue’s row and another pass’s row all answer the same 404 NOT_FOUND. 422 OVERRIDE_APPLIED when the row already charged a payment — it can no longer be edited. 400 INVALID_RANGE when the window ends before it starts. The answer is the row as stored, not the request echoed back.
Parameters, scopes and examples
Required scopes
billing.manage
Path parameters
passIdstring · required
Pass UUID
overrideIdstring · required
Payment override UUID
The corrected amount and window, plus an optional notify selection
Client-account parity P4 (A28): removes a not-yet-charged override so that period returns to the normal price. Requires billing.manage, an Idempotency-Key (bound to this pass and this override) and capability subscription_overrides. Same 404 NOT_FOUND rule as the PATCH. 422 OVERRIDE_APPLIED when the row already charged a payment — it cannot be removed retroactively. The body may be empty; send one only to choose notification channels.
Parameters, scopes and examples
Required scopes
billing.manage
Path parameters
passIdstring · required
Pass UUID
overrideIdstring · required
Payment override UUID
No fields required; send a notify selection only if the client should hear
Client-account parity P4 (A28): the panel’s multi-select writes. action "set" gives every selected period the same agreed amount — periods that already have an override are updated, base periods get a new row, and if the update fails the rows just inserted are removed again so the batch leaves nothing behind (which is why 500 DB_ERROR frees the key). action "restore" deletes the selected overrides so those periods return to the normal price, and is offered only when every selected period IS an override. Requires billing.manage, an Idempotency-Key and capability subscription_overrides. At most six periods or ids per call. 400 INVALID_RANGE for duplicate or inverted periods; 409 OVERRIDE_CONFLICT when a selected row no longer exists or a new period already has one — refresh and try again; 422 OVERRIDE_APPLIED when a selected row already charged a payment.
Parameters, scopes and examples
Required scopes
billing.manage
Path parameters
passIdstring · required
Pass UUID
Either the selected periods and one amount, or the override ids to restore
List the membership types this membership can convert to
Client-account parity P4 (A31): the venue’s other live recurring membership types, name-ordered, with each one’s price in the venue’s minor units and its billing cadence. The membership’s own type is excluded, and so are archived types — the change engine refuses those anyway, so offering one would be a dead end. Requires billing.manage and capability change_plan (a non-recurring, terminal or unfinished membership answers 422 PASS_ACTION_NOT_ELIGIBLE).
Client-account parity P4 (A31): quotes the change through the canonical membership-change engine and returns its object verbatim — the same shape GET /api/v1/admin/memberships/change already serves, including the signed, short-lived `quote` the confirm call must hand back. READ-ONLY: it changes nothing, needs NO Idempotency-Key, and runs on the read rate limit. Requires billing.manage and capability change_plan. This editor never offers a custom price, so the quote’s override_amount_minor is always null. Engine refusals map exactly as the memberships/change route maps them (404 / 403 / 409 / 422 / 402 / 503).
Client-account parity P4 (A31): applies the change the preview quoted. Requires billing.manage, an Idempotency-Key and capability change_plan, and the exact signed quote from the preview — its override_amount_minor MUST be null. Both this route and POST /api/v1/admin/memberships/change converge on the same durable quote fingerprint, so a change started on one cannot double-apply on the other. `replayed` is true when a repeated confirmation converged on an already-applied change; the client is not told twice. 409 QUOTE_STALE and the 422s free the key so the app can re-preview; 402 CHARGE_FAILED, 503 PARTIAL_APPLY, 503 CHANGE_IN_PROGRESS and 503 DATABASE_ERROR KEEP it, because the engine may already have moved money or claimed the quote.
Parameters, scopes and examples
Required scopes
billing.manage
Path parameters
passIdstring · required
Pass UUID
The target type, the signed quote from the preview, and an optional notify selection
Business POS read for an active venue client. Returns sanitized card references only (brand, last4, expiry, default, expired, chargeable); imported display-only cards are explicitly non-chargeable. Requires pos.access (register) OR members.contact (client record, the web admin gate). Never returns customer IDs, processor metadata, full card data, or client secrets.
Business POS card-setup operation for an active venue client. Requires an in-person consent attestation and Idempotency-Key. Returns the SetupIntent client secret, exact Stripe account namespace, legal merchant country, and frozen regional revision for native Payment Sheet.
Update the member phone number after an explicit staff confirmation. Requires members.edit and writes an audit record.
Parameters, scopes and examples
Required scopes
write:members
Path parameters
idstring · required
Member user ID
Supported member profile fields
Request body
{
"phone": "+4512345678"
}
POST/api/v1/admin/members/{id}/creditsBearer or API key
Issue account credit
Grant account credit to an active member (positive manual adjustment) on the same atomic, organization-scoped ledger path as the web action. The response balance is the canonical venue-available balance; profiles.credit_balance is maintained only as an account-wide compatibility cache. Currency must equal the venue currency (422 CURRENCY_MISMATCH). Client delivery is silent by default and requires an explicit canonical Email/SMS/Push selection plus notifications.send; membership and unavailable selected channels are rejected before the balance changes, and legacy booleans remain silent. Idempotency-Key is required (1–255 characters): an identical retry returns the original transaction and balance, while reuse for a different semantic request returns 409 IDEMPOTENCY_KEY_REUSE_MISMATCH.
Venue-wide duplicate suggestions for the clients-list merge wizard. Matches are never merged automatically. Requires members.merge plus members.contact and the acting membership’s full client-contact visibility.
Ranked duplicate candidates for the current client in this venue, excluding dismissed pairs. Requires members.merge plus members.contact and the acting membership’s full client-contact visibility.
Dismiss one candidate pair for this venue. Body: { candidate_user_id }. Idempotency-Key required; concurrent reuse is serialized before mutation. Requires members.merge plus members.contact and the acting membership’s full client-contact visibility, matching candidate review.
Fail-closed, venue-scoped merge preview with explicit transfer, retained-identity, indirect invoice descendant, and unsupported-data blocker counts. Unknown or unreadable ownership is never reported as zero. Body: { primary_user_id, secondary_user_id }. The path id must be one of those two. Requires members.merge plus members.contact and full membership contact visibility. Merge is notification-silent.
Parameters, scopes and examples
Required scopes
members.mergemembers.contact
Path parameters
idstring · required
Primary or secondary member ID
POST/api/v1/admin/members/{id}/mergeBearer token
Merge duplicate client profiles
Merges the secondary profile into the primary survivor using the transactional admin_merge_venue_profiles guard. Unsupported data, concurrent setup operations, provider-wallet/subscription ownership changes, and material row conflicts roll back without deactivating the source; retained identity/audit data remains explicit. confirm_token must be the literal MERGE. Notification-silent. Idempotency-Key is bound to venue, path member, and canonical body, with an atomic pre-mutation claim that serializes concurrent reuse. Requires members.merge plus members.contact and full membership contact visibility.
Parameters, scopes and examples
Required scopes
members.mergemembers.contact
Path parameters
idstring · required
Primary or secondary member ID
Survivor, merged-away profile, field choices, and MERGE confirmation
Issues one frozen-term comeback promise per client/template/settlement day, including after that promise closes. Delivery is silent by default; an explicit notify audience and channel set plus notifications.send is required to contact the client. Every selected channel is preflighted before the offer is created and the exact selection only narrows delivery—an SMS-only request can never fall back to email. The response reports actual delivery, and crash-safe deterministic provider/channel dedup permits a silent existing offer to be notified without double-send. Idempotency-Key is bound to venue, path member, and canonical body and atomically claimed before mutation. Permission: loyalty_price.grant.
Mark an open offer as declined (they said no) or revoked (withdrawn). Idempotency-Key required and atomically claimed before mutation. Permission: loyalty_price.grant.
Client-record invoices for this member. Drafts are excluded. Each row includes status, totals, and a share_url for the public invoice page. Permission: invoices.view. Org-wide invoice management remains /admin/invoices.
Venue-scoped family/partner/guest relationships for this client. Permission: members.view_insights. Related email fields are returned only when the caller also holds members.contact and full membership contact visibility; otherwise they are null.
Parameters, scopes and examples
Required scopes
members.view_insights
Path parameters
idstring · required
Active member ID
POST/api/v1/admin/members/{id}/tagsBearer token
Add a client tag
Add a manual member tag. Idempotency-Key required and atomically claimed before mutation. Permission: members.edit.
Parameters, scopes and examples
Required scopes
members.edit
Path parameters
idstring · required
Active member ID
DELETE/api/v1/admin/members/{id}/tagsBearer token
Remove a client tag
Remove a member tag. The tag travels in the query string (?tag=). Permission: members.edit.
Parameters, scopes and examples
Required scopes
members.edit
Path parameters
idstring · required
Active member ID
Query parameters
tagstring · required
Tag to remove
GET/api/v1/admin/bookingsBearer or API key
All bookings
Venue-wide booking list with filtering by date, status, class, member, and location.
Parameters, scopes and examples
Required scopes
read:bookings
Query parameters
fromstring
Start date (YYYY-MM-DD)
tostring
End date (YYYY-MM-DD)
statusstring
Filter by booking status
class_instance_idstring
Filter by class instance
user_idstring
Filter by member
location_idstring
Filter by location
POST/api/v1/admin/bookingsBearer or API key
Book for member
Create a confirmed or waitlisted booking on behalf of a member. Supports capacity override for admins. Client delivery is silent by default and requires an explicit Email/SMS/Push selection plus notifications.send; unavailable selected channels are rejected before booking/pass/count effects. The engine automatic notice is suppressed for this admin route so omission cannot send.
POST/api/v1/admin/bookings/waitlistBearer or API key
Add member to waitlist
Manually place a member on a class's waitlist at the queue tail. Always creates a waitlisted booking (never auto-confirms). Client delivery is default-silent and requires both an explicit client audience/channel selection and notifications.send; unavailable selected channels are rejected before queue effects.
DELETE/api/v1/admin/bookings/waitlist/{bookingId}Bearer or API key
Remove member from waitlist
Remove a waitlisted booking. Waitlisted rows only (409 on a confirmed booking); never triggers auto-promotion. Idempotent. Client delivery is default-silent and requires both an explicit client audience/channel selection and notifications.send; unavailable selected channels are rejected before queue effects.
Parameters, scopes and examples
Required scopes
write:bookings
Path parameters
bookingIdstring · required
Waitlisted booking ID
Optional reason and explicit client notification channels. Omit notify to stay silent.
POST/api/v1/admin/bookings/{bookingId}/cancelBearer or API key
Cancel a confirmed booking
Cancel a member's confirmed booking on their behalf (admin cancel semantics — may charge late fees + restore clips per policy; NOT the fee-free lapsed-booking path). Decrements booked_count, writes audit + booking.cancelled webhook, and issues a 30s undo ticket. Client delivery is silent by default and requires an explicit Email/SMS/Push selection plus notifications.send; unavailable selected channels are rejected before any mutation; legacy notify_client remains silent. Idempotent via Idempotency-Key. Returns 409 ALREADY_CANCELLED on a cancelled booking and 409 ON_WAITLIST for a waitlisted row (use the waitlist remove endpoint).
Bearer-JWT only. Correct one past class booking through the protected, organization-scoped historical ledger. Requires scheduling.manage_history plus the ordinary operation permission (bookings.manage for retrocreate, booking.checkin for attendance, booking.checkin + bookings.mark_no_show for no-show, or booking.cancel_member for cancellation/invalidation). Idempotency-Key must be a UUID. expected_class_updated_at is a compare-and-set token mandatory for every operation; expected_updated_at is a compare-and-set token additionally required for existing-booking operations. The executor refuses linked financial, pass, guest, waitlist, external, and other unsafe effects rather than inventing compensating semantics. Historical correction delivery is explicitly default-silent: Email, SMS, and Push are not available for this operation; a canonical explicit selection first requires notifications.send and then returns 422 before any mutation. Until the protected database executor has been installed, the route returns 503 HISTORICAL_EXECUTOR_UNAVAILABLE and never substitutes table calls.
Parameters, scopes and examples
Required scopes
scheduling.manage_history
Path parameters
bookingIdstring · required
Class booking ID
One closed class-booking correction. Every operation requires expected_class_updated_at; existing-booking operations also require expected_updated_at. history_confirmation_token must be REWRITE.
Bearer-JWT, passes.manage-scoped server-authoritative preview for a recurring membership. Resolves the selected client and saved card in the active venue, validates the venue-local start-date policy, and returns canonical buyer-specific gross pricing, registration fee/waiver, due-today amount, access date, first charge, next renewal, card label, contract/terms summary, and — when an operator discount schedule is requested — the resolved discount_schedule block with the agreed amount, the number of discounted periods and the first full-price charge date. This endpoint never mutates or charges.
Parameters, scopes and examples
Required scopes
passes.manage
Recurring membership options with exactly one of saved_payment_method_id (card-collected) or external_tender_method (venue-collected renewals, billing_mode=external). Optional registration_fee_discount applies a per-sale percentage discount to the one-time registration fee; legacy waive_registration_fee remains accepted. Optional discount_schedule sets an operator-agreed price for the first period, a fixed number of periods, or for as long as the membership runs; discount_reason stores the staff rationale with review, Stripe metadata, audit, and rate records.
Bearer-JWT, passes.manage-scoped recurring membership creation through the canonical subscription checkout core. Idempotency-Key is required. Re-resolves pricing, dates, saved-card ownership, Stripe locality, VAT/age band, concessions, and legal delivery before mutation. Returns the exact preview plus pass/subscription ids, payment status, and contract-delivery result. Off-session declines and SCA requirements are explicit 402 responses.
Parameters, scopes and examples
Required scopes
passes.manage
The same server-authoritative options accepted by the preview endpoint
Bearer-JWT, passes.manage-scoped option list for an organization-owned pass. Each option is buyer-priced by the canonical membership-change quote engine; a failed target is reported separately and cannot hide valid sibling options. Query: pass_id.
Parameters, scopes and examples
Required scopes
passes.manage
GET/api/v1/admin/memberships/changeBearer token
Preview a client membership change
Bearer-JWT, passes.manage-scoped server quote. Query: pass_id, target_pass_type_id and optional override_price_major. Returns exact charge, credit, effective date, next renewal and a short-lived signed quote binding; it never mutates or charges.
Parameters, scopes and examples
Required scopes
passes.manage
POST/api/v1/admin/memberships/changeBearer token
Apply a client membership change
Bearer-JWT, passes.manage-scoped confirmation through the canonical membership-change core. Requires Idempotency-Key and the exact signed quote returned by preview; foreign-venue passes resolve as not found and client-supplied prices are not accepted.
Process an idempotent point-of-sale transaction. Supports cash, venue credit, and a server-validated saved card. Saved-card SCA returns a 202 challenge response and is completed with a separate idempotent finalize request. Fresh cards, MobilePay and Stripe Terminal use their dedicated flows.
Recent POS transactions filtered by location and date. Refund headroom subtracts both succeeded and in-flight operation claims; refunded_amount reports succeeded claims and pending_refund_amount reports the reserved in-flight amount.
Parameters, scopes and examples
Required scopes
read:payments
POST/api/v1/admin/pos/transactions/{id}/receiptBearer or API key
Resend POS receipt
Resend a tenant-scoped POS transaction receipt by email or SMS. Uses the client's stored contact unless an explicit recipient is supplied. Idempotency-Key is required and retries of the same delivery action must reuse it.
Parameters, scopes and examples
Required scopes
write:pos
Path parameters
idstring · required
POS transaction ID
Receipt delivery channel and optional recipient override
Request body
{
"method": "email"
}
GET/api/v1/admin/pos/summaryBearer or API key
Daily POS sales summary
Daily sales breakdown for the given date (default today): totals (gross/discounts/VAT/credits/net) plus per-payment-method and per-transaction-type buckets. Completed transactions only; same date-window semantics as /admin/pos/recent.
Parameters, scopes and examples
Required scopes
read:payments
Query parameters
datestring
YYYY-MM-DD (default today)
location_idstring
Filter by location
GET/api/v1/admin/dashboard/revenue-seriesBearer or API key
Daily revenue series (sparkline)
Zero-filled daily revenue series ending today — succeeded payments bucketed by UTC day, matching the dashboard revenue_today semantics. days clamps to 1–90 (mobile uses 7 and 30).
Parameters, scopes and examples
Required scopes
read:reports
Query parameters
daysinteger
Window length in days (1–90)Default: 7
POST/api/v1/admin/members/{id}/membership/pauseBearer or API key
Pause membership
Pause (freeze) a member’s pass for a date window. Validated against the pass type’s pause policy; recurring memberships receive exact per-cycle billing credits on their own Stripe account; audit_log pass_paused. Client delivery is silent by default and requires notify.audience.clients=true plus explicit Email/SMS/Push channels and notifications.send. Legacy notification booleans remain silent. Selected-channel availability is rechecked before mutation; exact unavailable/preflight-failed responses leave the membership unchanged. Idempotency-Key honored. {id} accepts UUID or display ID (e.g. HYC-0042).
POST/api/v1/admin/members/{id}/membership/resumeBearer or API key
Resume membership
Resume a paused pass (Stripe-first ordering with compensating re-pause). Audit_log pass_resumed. Client delivery is silent by default and uses only an explicit canonical Email/SMS/Push selection. Selected-channel availability is rechecked before mutation; exact unavailable/preflight-failed responses leave the membership unchanged. Idempotency-Key honored.
Parameters, scopes and examples
Required scopes
write:members
Path parameters
idstring · required
Member user ID or display ID
Resume immediately or from a venue-local date. An unproven Stripe pause stays blocked unless acknowledge_unproven_pause types CLEAR PAUSE plus a reason. Optional explicit client delivery.
POST/api/v1/admin/members/{id}/membership/terminateBearer or API key
Cancel / terminate membership
Cancel or terminate a recurring membership with explicit effective dates: mode period_end (cancel at current cycle end), chosen_cycle (kth upcoming cycle, cycle required), or immediate. Runs the kill-switch-gated termination engine (fail-closed Stripe). Response carries the engine-confirmed effective_at. Client delivery is silent by default and requires an explicit canonical channel choice plus notifications.send; legacy booleans remain silent. Selected-channel availability is rechecked before mutation; exact unavailable/preflight-failed responses leave the membership unchanged. Idempotency-Key honored.
GET/api/v1/admin/members/{id}/membership/termination-previewBearer or API key
Termination preview (cycle picker)
Next 6 cycle boundaries (effective_at, venue-local last usable day, precedes-binding flag), venue policy defaults, and billing horizon for the terminate endpoint’s cycle picker.
Parameters, scopes and examples
Required scopes
read:members
Path parameters
idstring · required
Member user ID or display ID
Query parameters
pass_idstring
Pass ID (uuid)
POST/api/v1/admin/terminal/connection-tokenBearer or API key
Stripe Terminal connection token
Mint a Stripe Terminal connection token plus the venue Terminal location id (`{secret, location_id}`) for card-present readers and Tap-to-Pay. Ephemeral-token fetch — no Idempotency-Key (the Terminal SDK always needs a fresh token).
Parameters, scopes and examples
Required scopes
write:pos
POST/api/v1/admin/terminal/payment-intentBearer or API key
Create Terminal payment intent
Create a card-present PaymentIntent (manual capture) on the venue connected account. Returns `{client_secret, payment_intent_id}`. Money mutation — send an Idempotency-Key; replays return the cached response and the key is forwarded to Stripe.
Parameters, scopes and examples
Required scopes
write:pos
Payment intent
Request body
{
"amount": 12000,
"currency": "DKK"
}
POST/api/v1/admin/terminal/captureBearer or API key
Capture Terminal payment
Capture a confirmed card-present PaymentIntent. Returns `{captured: true, payment_intent_id}`. Money mutation — send an Idempotency-Key; an already-captured intent returns success.
Parameters, scopes and examples
Required scopes
write:pos
Capture
Request body
{
"payment_intent_id": "pi_xxx"
}
POST/api/v1/admin/terminal/receiptBearer or API key
Send Terminal receipt
Email or SMS a receipt for a captured Tap-to-Pay sale, resolved from the Stripe payment intent id. TERM-IDEMP-01: requires a caller-scoped Idempotency-Key; a replayed key returns the cached terminal response instead of re-sending.
POST/api/v1/admin/notifications/broadcastBearer or API key
Send broadcast
Send one or more push, email, and SMS channels to all members, selected member ids, or a server-resolved tag/pass/class audience. Idempotency-Key is required. Each channel derives a stable per-recipient delivery reference; a partial retry skips terminal successes/suppressions and resumes failed legs. Returns sent/skipped/failed counts per channel.
Parameters, scopes and examples
Required scopes
write:notifications
Broadcast
Request body
{
"channels": [
"email",
"sms"
],
"title": "New class added",
"target": {
"type": "tag",
"tag": "vip"
},
"subject": "New class added",
"body": "Check out our new Hot Power class on Saturday!"
}
GET/api/v1/admin/notifications/recentBearer or API key
Recent notifications
Recent email, SMS, and push notifications sent by the venue.
Parameters, scopes and examples
Required scopes
read:notifications
POST/api/v1/admin/staff/inviteBearer token
Invite a staff member
PROMPT_02 (S1-03) — provisions the auth user + profile + membership (status=invited), mints a staff_invitations claim token, and emails the venue-branded /auth/claim-invite link. Permission: staff.manage. The token is consumed by the WEB claim page (set password → membership flips invited→active); there is no separate accept API endpoint because the claim sets a password. 409 EMAIL_EXISTS when a Booking Bible account already exists for the email (adding an existing user as staff is a role change — use the admin UI). location_ids is stored on the invitation for record-keeping; location assignment remains a post-onboarding admin action. Emits staff.invited. Idempotency-Key supported.
Every staff shift in the venue for a date range. Permission: staff_scheduling.view. Joins staff profile name. Optional ?status= filter.
Parameters, scopes and examples
Query parameters
fromstring
Start ISO datetimeDefault: -7 days
tostring
End ISO datetimeDefault: +14 days
statusstring
Filter by ShiftStatus
POST/api/v1/admin/staff-scheduleBearer token
Create a staff shift
Create a new shift. Permission: staff_scheduling.manage. Note: API path skips the engine compliance pre-checks; for full compliance use the admin panel or the createShift server action.
POST/api/v1/admin/walk-in-queue/{id}/callBearer or API key
Call queue entry
Mark a queue entry as called and notify the client by SMS if they opted in. Permission: bookings.manage.
Parameters, scopes and examples
Required scopes
write:bookings
Path parameters
idstring · required
Queue entry ID
DELETE/api/v1/admin/walk-in-queue/{id}Bearer or API key
Remove walk-in
Cancel/remove a walk-in queue entry. Permission: bookings.manage.
Parameters, scopes and examples
Required scopes
write:bookings
Path parameters
idstring · required
Queue entry ID
GET/api/v1/admin/checkin/{classInstanceId}/qr-tokenBearer or API key
Get check-in QR token
Returns the current rotating QR token for a class instance. A new token is generated if none exists or the existing one is expired. Force rotation with ?refresh=true. Token TTL: 5 minutes. Permission: booking.checkin.
Accepts exactly one credential: Bearer JWT with `rates.view`, or an API key bound to the venue with `read:passes`. Dual credentials are rejected before authentication.
Accepts exactly one credential: Bearer JWT with `rates.view`, or an API key bound to the venue with `read:passes`. Dual credentials are rejected before authentication.
GET/api/v1/admin/rates/distributionBearer or API key
Get revenue distribution by rate
Accepts exactly one credential: Bearer JWT with `rates.view`, or an API key bound to the venue with `read:passes`. Dual credentials are rejected before authentication.
Accepts exactly one credential: Bearer JWT with `rates.view`, or an API key bound to the venue with `read:passes`. Dual credentials are rejected before authentication.
Accepts exactly one credential: Bearer JWT with `rates.view`, or an API key bound to the venue with `read:passes`. Dual credentials are rejected before authentication. The client and pass type must both belong to that venue.
Accepts exactly one credential: Bearer JWT with `rates.override`, or an API key bound to the venue with `write:passes`. Dual credentials are rejected before authentication.
DELETE/api/v1/admin/rates/override/{passId}Bearer or API key
Clear a pass rate override
Accepts exactly one credential: Bearer JWT with `rates.override`, or an API key bound to the venue with `write:passes`. Dual credentials are rejected before authentication.
Parameters, scopes and examples
Required scopes
write:passes
Path parameters
passIdstring · required
Pass id
Response example
{
"data": {
"ok": true
},
"error": null
}
GET/api/v1/admin/private-eventsBearer or API key
List private-event bookings
Accepts exactly one credential: Bearer JWT with `private_events.view`, or an API key bound to the venue with `read:private_events`. Targets are non-enumerating and venue-scoped.
Accepts exactly one credential: Bearer JWT with `private_events.manage`, or an API key bound to the venue with `write:private_events`. The canonical venue-scoped mutation workflow is used.
Parameters, scopes and examples
Required scopes
write:private_events
Private-event booking. Every PS-B2 field below is OPTIONAL and additive: client_id / save_as_client (link or create the client the session is for), partner_id + billing_target/billing_address/billing_vat_number/po_number/department/cost_center (bill a company — the billing block prefills from the partner record), brand_id, location_id, staff_note (a message the client sees), pricing_override ({mode: per_person|total, amount} — total is VAT-inclusive), and start_mode (confirmed | inquiry | confirm_on_payment) with payment_due_at. The legacy `status` field keeps working.
GET/api/v1/admin/private-events/{id}Bearer or API key
Get a private-event booking
Accepts exactly one credential: Bearer JWT with `private_events.view`, or an API key bound to the venue with `read:private_events`. Targets are non-enumerating and venue-scoped.
PATCH/api/v1/admin/private-events/{id}Bearer or API key
Update a private-event booking
Accepts exactly one credential: Bearer JWT with `private_events.manage`, or an API key bound to the venue with `write:private_events`. The canonical venue-scoped mutation workflow is used.
Parameters, scopes and examples
Required scopes
write:private_events
Path parameters
idstring · required
Booking id
Fields to update. PS-B2 adds the same optional fields the create route takes (client_id, partner_id + billing block, brand_id, location_id, staff_note, pricing_override, payment_due_at) plus `reprice` (recompute the frozen subtotal/VAT/total/deposit) and `notify_client` ({enabled, channels}). The response carries the client-visible change summary.
POST/api/v1/admin/private-events/{id}/approveBearer or API key
Approve a private-event booking
Accepts exactly one credential: Bearer JWT with `private_events.manage`, or an API key bound to the venue with `write:private_events`. The canonical venue-scoped mutation workflow is used.
POST/api/v1/admin/private-events/{id}/cancelBearer or API key
Cancel a private-event booking
Accepts exactly one credential: Bearer JWT with `private_events.manage`, or an API key bound to the venue with `write:private_events`. The canonical venue-scoped mutation workflow is used.
POST/api/v1/admin/private-events/{id}/quoteBearer or API key
Send a private-event quote
Accepts exactly one credential: Bearer JWT with `private_events.manage`, or an API key bound to the venue with `write:private_events`. The canonical venue-scoped mutation workflow is used.
Accepts exactly one credential: Bearer JWT with `loyalty_price.manage`, or an API key bound to the venue with `read:passes`. Dual credentials are rejected before authentication.
GET/api/v1/admin/loyalty-price/members/{memberId}Bearer or API key
Get a client loyalty-price context for a grant-capable operator
Accepts exactly one credential: Bearer JWT with `loyalty_price.grant`, or an API key bound to the venue with `write:passes`. Returns only the named active member’s programme label and standing; venue-wide configuration and aggregate counts remain manage-only.
POST/api/v1/admin/loyalty-price/grantBearer or API key
Give a client the loyalty price
Accepts exactly one credential: Bearer JWT with `loyalty_price.grant`, or an API key bound to the venue with `write:passes`. Requires a UUID `Idempotency-Key`; a replay is bound to the same venue, caller and grant payload. Writes through the context-free grant core (never a cookie action), so the audit trail and status recompute are identical to the admin web surface.
Parameters, scopes and examples
Required scopes
write:passes
user_id + a short reason. Optional venue-local expiry, and an optional pass_id with an agreed price on the catalog (MAJOR) scale — pass_price_override requires pass_id.
Request body
{
"user_id": "uuid",
"reason": "Agreed with the owner at the desk",
"expires_on": "2027-01-31",
"pass_id": "uuid",
"pass_price_override": 249
}
List every event on the unified calendar (classes, appointments, private events, streams, blocked time, instructor unavailability, blackouts, room rentals, maintenance, staff shifts, open gym) in a date range. JWT (any staff role) returns the org feed; API key with read:calendar returns the same. Filters: room_id, staff_id, location_id, brand_id, sources (comma-separated), only_blocking.
List maintenance slots in a date range. API key with read:maintenance scope. Filters: start, end (ISO datetime), room_id, status, limit (1-200, default 50).
Create a maintenance slot. API key with write:maintenance. Body: maintenance_type (preventive | corrective | inspection | deep_clean | equipment | renovation), title, start_time, end_time, plus optional priority, room_id, equipment_id, blocks_room (default true), assigned_staff_id, vendor_name, vendor_contact, estimated_cost, notes. Idempotency-Key header honored. When blocks_room is true and a room is set, conflicts against classes / appointments / private events / streams / room rentals / other maintenance return 409 with the conflict list. Emits maintenance.scheduled.
Parameters, scopes and examples
Required scopes
write:maintenance
Maintenance creation payload
Request body
{
"maintenance_type": "deep_clean",
"title": "Quarterly studio deep clean",
"start_time": "2026-05-01T20:00:00Z",
"end_time": "2026-05-01T22:00:00Z",
"room_id": "uuid",
"priority": "normal",
"blocks_room": true
}
Staff33 documented operations
GET/api/v1/staff/scheduleBearer token
My teaching schedule
Instructor's classes. Optional scope=own|partner|all; every row includes origin venue metadata and origin.timezone so apps bucket collaboration classes in the owning venue's local day.
GET/api/v1/staff/earningsBearer token
My earnings
Compensation, tips, and commissions broken down by period and class. tips_settled_via_collaboration is additive visibility for gratuities paid on a practitioner statement and is deliberately excluded from tips_received and total.
GET/api/v1/staff/classes/{id}/rosterBearer token
Class roster
View attendee list for a class the instructor is assigned to. Returns class and booking updated_at CAS tokens, venue-local day_state, and historical_capabilities. include_historical_records=true additionally exposes terminal roster rows and requires scheduling.manage_history.
Business-app staff correction using the same atomic ledger as Admin. Requires an active venue, roster access, class assignment where applicable, scheduling.manage_history, and the ordinary operation permission. Every operation requires expected_class_updated_at; existing rows also require expected_updated_at. Delivery is default-silent.
Parameters, scopes and examples
Path parameters
idstring · required
Class instance ID
bookingIdstring · required
Class booking ID
Bounded historical roster correction with UUID Idempotency-Key
Provider-scoped atomic correction for an isolated past class. The active provider must be assigned to the existing class; retrocreate must assign that provider directly, and assignment corrections must retain them. Requires schedule.view_own, scheduling.manage_history, scheduling.manage, UUID Idempotency-Key, CAS evidence for existing rows, past effective_at, and typed REWRITE. Notifications are always silent.
Mark or unmark a participant present for a course session ({user_id, present}). Idempotent; writes the same attendance store the web roster uses. Supports Idempotency-Key.
Send an email or SMS to course participants (audiences: enrolled, waitlisted, all, by track, by payment status, hand-picked). Requires course-manage scope; rate-limited; supports Idempotency-Key (retries never double-send).
Parameters, scopes and examples
Path parameters
idstring · required
Course ID
Message
Request body
{
"channel": "email",
"subject": "Bring a mat tomorrow",
"message": "Hi everyone — please bring your own mat to tomorrow’s session.",
"audience": {
"kind": "enrolled"
}
}
GET/api/v1/staff/availabilityBearer token
List my unavailable dates
Calling staff member's current and future unavailable dates for the selected venue. Permission: staff_portal.availability.
POST/api/v1/staff/availabilityBearer token
Set availability
Add or update unavailable dates for the calling staff member. Permission: staff_portal.availability.
Strict partial update of a current/future window using the exact updated_at token returned by GET. Caller must own the window; another instructor requires staff.edit. A stale token returns 409 STALE_TARGET. Existing or target ranges touching venue-local history fail closed until the dedicated executor is installed.
Parameters, scopes and examples
Path parameters
idstring · required
Window ID
Concurrency token plus one or more changed window fields
Sets is_active=false on a current/future window using the exact updated_at token returned by GET, after tenant and owner-or-staff.edit authorization. A stale token returns 409 STALE_TARGET. Historical ranges fail closed until the dedicated executor is installed.
Atomic, immutable-ledger correction for a past recurring availability window owned by the active staff member. Requires availability.manage_history, staff_portal.availability, UUID Idempotency-Key, expected_updated_at plus expected_is_active for existing rows, a past effective_at, and typed REWRITE. Notifications are always silent.
Parameters, scopes and examples
Path parameters
idstring · required
Availability window ID
Self-owned historical availability correction
Request body
{
"operation": "availability_window.invalidate",
"expected_updated_at": "2026-08-20T09:00:00.000Z",
"expected_is_active": true,
"history_reason": "Approved rota confirms that this window did not apply",
"history_confirmation_token": "REWRITE",
"effective_at": "2026-08-10T10:00:00.000Z",
"intent": {}
}
GET/api/v1/staff/substitute-poolBearer token
Read substitute-pool opt-in
Returns { enabled, updated_at } for the calling user's active org.
PUT/api/v1/staff/substitute-poolBearer token
Toggle substitute-pool opt-in
Set whether the calling user is available to be auto-suggested as a substitute. Body: { enabled: boolean }. Emits substitute_pool.opt_in_changed.
Parameters, scopes and examples
Opt-in state
Request body
{
"enabled": true
}
GET/api/v1/staff/appointmentsBearer token
My appointments
Cursor-paginated list of the calling provider's appointments, including venue currency and the same client name and 80-character provider-note preview shown in the web staff list. Contact details are not exposed. Query params: cursor (opaque next_cursor; legacy ISO timestamps are temporarily accepted), limit (1..100, default 25), status (one of the appointment status strings), direction (upcoming|past, default upcoming).
Parameters, scopes and examples
Query parameters
cursorstring
Opaque next_cursor returned by the previous page
limitnumber
Page size (1..100)Default: 25
statusstring
Optional status filter
directionstring
upcoming | pastDefault: upcoming
GET/api/v1/staff/appointments/{id}Bearer token
My assigned appointment detail
Provider-scoped detail with updated_at CAS evidence, server-authoritative historical review flags, privacy-gated client contact fields, and exact per-channel notification availability. The route always binds provider_id to the caller.
PATCH/api/v1/staff/appointments/{id}Bearer token
Act on my assigned appointment
Provider-scoped check_in, start, complete, no_show, cancel, or reschedule. Requires the action-specific appointments.*_own permission, exact expected_updated_at, and Idempotency-Key. Client notifications are default-silent and require explicit notification_channels plus notifications.send and server preflight. Past/terminal mutations fail closed until the appointment historical executor is installed.
Parameters, scopes and examples
Exact provider lifecycle intent and caller-rendered concurrency snapshot
Provider-scoped form of the dedicated atomic appointment-history command. Requires staff_portal.appointments, appointments.manage_history, the ordinary operation permission, a UUID Idempotency-Key, REWRITE attestation, and exact expected_updated_at plus expected_status CAS for existing rows. The existing appointment and any retrocreate or assignment target must remain assigned to the active provider. Corrections are always silent. Returns 503 HISTORICAL_EXECUTOR_UNAVAILABLE without table-call fallback until correct_appointment_historical is installed.
Parameters, scopes and examples
Required scopes
appointments.manage_history
Path parameters
idstring · required
Appointment UUID
The same closed appointment correction body as the admin route
Calling staff member's own non-instructor shifts (reception, cleaning, manager, front desk) in a date range. Use ?from=&to= ISO datetimes; defaults to next 14 days.
Parameters, scopes and examples
Query parameters
fromstring
Start ISO datetimeDefault: now
tostring
End ISO datetimeDefault: +14 days
POST/api/v1/staff/shifts/clock-inBearer token
Clock in
Clock in to an own staff shift. Allowed from 15 min before scheduled start through 30 min after. Sets status to in_progress and stamps clock_in_at. Emits shift.clock_in.
Parameters, scopes and examples
Shift to clock in to
Request body
{
"shift_id": "uuid"
}
POST/api/v1/staff/shiftsAPI key
Create a staff shift
Create a non-instructor staff shift. API-key only (write:staff). Body: start_time, end_time, optional staff_id, shift_type (regular | overtime | on_call | training | meeting), break_minutes, role_required, location_id, hourly_rate, notes. Idempotency-Key header honored. Emits shift.created (and shift.assigned if a staff_id is set).
Soft-cancel a shift (sets status=cancelled, preserves audit/payroll references). JWT (admin/manager) or API key with write:staff. Use ?reason= to attach a cancellation reason to the audit row.
Parameters, scopes and examples
Required scopes
write:staff
Path parameters
idstring · required
Shift UUID
Query parameters
reasonstring
Cancellation reason (free text)
POST/api/v1/staff/clockBearer token
Clock in or out (unified)
Unified clock-in/out endpoint. JWT only — resolves the staff member from the session token. Body: { action: "in" | "out", shift_id }. On clock-out the response includes actual_hours and total_pay. Emits shift.clock_in or shift.clock_out.
Parameters, scopes and examples
Clock action
Request body
{
"action": "in",
"shift_id": "uuid"
}
POST/api/v1/staff/shifts/clock-outBearer token
Clock out
Clock out of an in-progress staff shift. Computes actual_hours, actual_break_minutes, and total_pay (when hourly_rate is set). Returns warnings for break/EU compliance issues. Emits shift.clock_out.
Parameters, scopes and examples
Shift to clock out of
Request body
{
"shift_id": "uuid"
}
GET/api/v1/staff/time-offBearer token
My time-off requests
Latest 100 own time-off requests across all statuses.
POST/api/v1/staff/time-offBearer token
Request time off
Submit a new time-off request. Always created with status=pending. Manager approval/decline happens via the admin panel. Emits time_off.requested.
Create or update a lead for the venue. Public rate-limited (10 req/min/IP) or API-key authenticated (write:leads). Upserts on (organization_id, email): provided fields populate blanks, existing non-null values are preserved. Fires the lead_captured analytics event and emits a lead.created webhook with the full record plus an attribution object (utm_*, fbclid, gclid, landing_page, referrer).
Parameters, scopes and examples
Required scopes
write:leads
Lead payload. Org resolves from API key > X-Organization-ID header > subdomain > organization_id.
List leads for the API key's organization. API key only (JWT not permitted). Requires the read:leads scope.
Parameters, scopes and examples
Required scopes
read:leads
Query parameters
searchstring
Search by email, first_name, or last_name
sourcestring
Filter by source (website_form, exit_intent, referral, etc.)
statusstring
Filter by status (new, contacted, converted, unsubscribed)
pageinteger
Page numberDefault: 1
limitinteger
Items per page (max 100)Default: 20
Events3 documented operations
POST/api/v1/eventsPublic
Track an analytics event
Record a server-side analytics event into user_events. Public rate-limited (60 req/min/IP) or API-key authenticated (write:events). For conversion event names (purchase, subscribe, refund, lead_captured) we additionally fire Meta CAPI + GA4 MP when the venue has pixel credentials configured.
Parameters, scopes and examples
Required scopes
write:events
Event payload. UTM + click-id + page URL get merged into event_properties.
Create an open-gym session for a client. Validates an active pass with allow_open_gym=true and the access schedule. JWT users self-check in; API keys must include user_id.
Parameters, scopes and examples
Required scopes
write:bookings
Optional location, source, pass override, and notes.
Returns the latest version of each active required waiver for the authenticated client in the validated X-Organization-ID venue, including the exact markdown body to display and the latest version the client signed. `body_md` is canonical; `body` is the native-app compatibility alias with the same value.
Removes the caller's own club membership. Idempotency-Key supported; leaving a club you are not in is a no-op success. Emits club.member_left (audit + webhook).
Parameters, scopes and examples
Path parameters
idstring · required
Club id
Response example
{
"data": {
"ok": true
},
"error": null
}
DELETE/api/v1/clubs/{id}/membershipBearer or API key
Leave a club (membership alias)
REST-shaped alias for POST /clubs/{id}/leave used by the mobile clubs contract — identical behavior (Idempotency-Key, no-op success when not a member, club.member_left emit).
Parameters, scopes and examples
Path parameters
idstring · required
Club id
Response example
{
"data": {
"ok": true
},
"error": null
}
GET/api/v1/clubs/suggestionsBearer or API key
List club suggestions (admin)
Admin queue of client-submitted club suggestions. Filter by status.
Returns a single chat channel summary (channel row + unread_count + last_message_at) for the authenticated org. Staff role (or chat.read scope) required.
Bulk Scan Session — map a single scanned barcode to a preset target
Hot-path endpoint for the rapid-mapping Bulk Scan Session. Each call maps one scanned barcode. Returns status=created (new mapping), duplicate_in_session (same barcode+target already exists), requires_confirmation (different target, resend with allow_overwrite=true to proceed), or overwritten.
Model Context Protocol server (HTTP transport, JSON-RPC 2.0, protocol 2025-03-26). Authenticate with `X-API-Key`. Methods: `initialize`, `ping`, `resources/list`, `resources/read`, `tools/list`, `tools/call`. Read-only in v1. See `/developers/mcp` for the full guide.
Returns every registered feature module with its resolved enabled/settings/source for the calling venue. Resolution honors the four-tier precedence (tenant override → group lock → venue → group default → plan → default). Mobile Business app uses this for parity with /admin/settings/features.
Parameters, scopes and examples
Response example
{
"data": [
{
"key": "leaderboards",
"label": "Leaderboards",
"description": "Member-facing leaderboards by class type, period, and metric.",
"category": "Community",
"enabled": true,
"source": "plan",
"locked_by_group": false,
"settings": {}
}
]
}
PATCH/api/v1/admin/features/[moduleKey]Bearer or API key
Update a venue-level feature toggle
Flip enabled/settings for a feature module at the venue tier. Idempotency-Key supported. Returns 400 with `Locked by group: <paths>` when the venue tries to flip a group-locked toggle or write to a group-locked dot-path in `settings`. Audit-logged + emits `feature_toggle.changed` webhook.
Parameters, scopes and examples
Required scopes
write:settings
Path parameters
moduleKeystring · required
Module key from feature_modules.key
Partial update — only the fields you want to change.
Browse partner venues available to the member across the BOOKING BIBLE network. Each entry exposes a public summary plus the exact relationship status, active partnership id, and venue-level bookable flag for the organization selected by X-Organization-ID. Member-JWT.
Book a class at a partner venue using a network-eligible pass. Resolves the legal gate against the HOST venue’s documents before booking. Requires a caller-stable `Idempotency-Key` header; exact retries return the original booking and visit. Member-JWT.
Creates a venue-to-venue Network partnership request through the context-free Network mutation core. Bearer JWT requires network.manage; API keys require write:network.
GET/api/v1/network/partnerships/{id}Bearer or API key
Read a venue Network partnership
Reads one venue-to-venue Network partnership after proving the caller belongs to either party. Bearer JWT requires network.view; API keys require read:network.
PATCH/api/v1/network/partnerships/{id}Bearer or API key
Update a venue Network partnership
Updates a venue-to-venue Network partnership through the commercial lifecycle. Bearer JWT requires network.manage. API keys with write:network may change lifecycle status, but agreement negotiation and terms revisions require a verified human JWT administrator and return 403 for API-key callers.
DELETE/api/v1/network/partnerships/{id}Bearer or API key
Terminate a venue Network partnership
Terminates immediately only when binding and notice have both elapsed; otherwise schedules termination through the locked service RPC. Bearer JWT requires network.manage; API keys require write:network.
GET/api/v1/network/partnerships/{id}/visitsBearer or API key
List visits for a venue Network partnership
Lists visit ledger rows for one venue-to-venue Network partnership after proving the caller belongs to either party. Bearer JWT requires network.view; API keys require read:network.
GET/api/v1/network/partnerships/{id}/settlementsBearer or API key
List settlements for a venue Network partnership
Lists network-only settlements for one venue-to-venue Network partnership after proving the caller belongs to either party. Bearer JWT requires network.view; API keys require read:network.
Lists collaborations for the workspace selected by X-Organization-ID. Returns venue-owned relationships and invitations received by an owned professional workspace. Supplying the exact email query performs the privacy-preserving invitation lookup and requires network.manage.
Creates a pending venue-to-professional collaboration with an explicit venue role and compensation model. Requires network.manage and a non-individual venue workspace.
Accepts or declines an invitation from the professional workspace, or lets the venue set role, pause, resume, or terminate the relationship. Every operation is tenant-checked and requires network.manage.
Ends the relationship through the canonical termination core and suspends its venue membership unless keep_membership is explicitly true. Either party may terminate; requires network.manage.
Parameters, scopes and examples
Optional termination reason and membership handling
List the caller’s relationships (bidirectional — both relationships the member created and ones pointing back at them), hydrated with the linked member’s profile. Unlocks family pricing, shared booking, and pass sharing. Member-JWT, org-scoped.
Add a relationship. When `related_email` matches a member in the same venue, the relationship links to their profile and a mirror row is written so both members see it. Member-JWT. Idempotency-Key supported.
Submit a private-event inquiry as the authenticated member. Validates participant count against the event type’s min/max, computes pricing, and inserts a `private_event_bookings` row with `booked_by` set; emits `private_event.inquiry_created`. Member-JWT, org from X-Organization-ID. Idempotency-Key supported.
PROMPT_11 — returns the Stripe `client_secret`, frozen `merchant_country_code`, and `stripe_account_id` (`acct_*` for a direct Connect PI, otherwise null) for the booking’s deposit/full charge so the member can initialize Stripe Elements in the exact payment context. Customer/ephemeral-key credentials are returned only when this member owns the customer frozen by the first payment operation; admin-created or another accepted booker identity receives a safe generic sheet with null customer credentials. Idempotent (reuses the frozen PaymentIntent execution created at confirmation). `{ skipped: true }` when the event type’s payment_mode is `none`. Member-JWT; ownership by contact_email.
Member approves a quoted booking and gets Payment Sheet credentials
The member’s “Approve & pay” CTA: the booker confirms a quote the venue sent (status `quoted`) and receives the same frozen PaymentIntent, `merchant_country_code`, and `stripe_account_id` (`acct_*` only for direct Connect; otherwise null). Customer/ephemeral-key credentials are returned only to the user id that owns the frozen Stripe customer; another accepted booking identity receives a generic sheet with null customer credentials. Member-JWT; ownership by `booked_by` or `contact_email`. Idempotency-Key header REQUIRED — a retry re-enters the repairable confirmation pipeline and returns the same frozen execution (no duplicate PI, account drift, or regional drift). Honors the event type’s payment_mode via the shared helper; `{ skipped: true }` when payment_mode is `none` (invoice path).
PROMPT_11 — public catalog of a venue’s active, publicly-listed private-session types for the embeddable widget (/embed/private-sessions). Cross-origin access is governed by the platform dynamic CORS allowlist (venue custom domains). Cached, IP-throttled.
List products for the authenticated venue. Business-app JWTs require pos.access; API keys require read:products. Archived products are hidden unless include_archived=true.
Parameters, scopes and examples
Required scopes
read:products
Query parameters
include_archivedboolean
Include archived products. Defaults to false.Default: false
POST/api/v1/admin/productsAPI key
Create a venue product
Create through the canonical product mutation contract. Unknown/protected fields are rejected; initial stock creates one movement.
Parameters, scopes and examples
Required scopes
write:products
GET/api/v1/admin/products/{id}Bearer or API key
Get a venue product
Get one product only when it belongs to the authenticated venue. Business-app JWTs require pos.access; API keys require read:products.
Parameters, scopes and examples
Required scopes
read:products
Path parameters
idstring · required
Product UUID
PATCH/api/v1/admin/products/{id}Bearer or API key
Update a venue product
Update mutable catalog fields through the canonical product core. Business-app JWTs require products.manage; API keys require write:products. Products and its tier-gated Point of Sale dependency must be active. stock_quantity and protected fields are rejected.
Parameters, scopes and examples
Required scopes
write:products
Path parameters
idstring · required
Product UUID
DELETE/api/v1/admin/products/{id}API key
Archive a venue product
Archive through canonical catalog semantics (is_active=false plus archived_at). Permanent deletion is separate and guarded.
Parameters, scopes and examples
Required scopes
write:products
Path parameters
idstring · required
Product UUID
GET/api/v1/venues/{slug}/product-packagesPublic
List buyable clip cards
Public catalog of active product passes (clip cards) for a venue. Cached, IP-throttled.
Initiate a product-pass (clip card) purchase. A nonempty Idempotency-Key header is required and defines the durable operation. Returns Customer/ephemeral-key credentials in the same frozen Stripe account as the PaymentIntent, plus `merchant_country_code` and `stripe_account_id` (`acct_*` only for direct Connect; otherwise null). A service-only pre-provider claim binds tenant, catalog, customer, regional, routing, fee, amount, and currency; `product_passes` is granted atomically on `payment_intent.succeeded`. Gated on the `products` module + venue legal docs. Member-JWT.
Buy N paid tickets for a community event (`{id}` is the event booking id). Price = `member_price` + `guest_price` × (ticket_count − 1). Requires an Idempotency-Key header. Returns Customer/ephemeral-key credentials in the same frozen Stripe account as the PaymentIntent, plus `merchant_country_code` and `stripe_account_id` (`acct_*` only for direct Connect; otherwise null). On `payment_intent.succeeded` the ticket order flips to paid and a `going` RSVP is upserted with `guest_count = ticket_count − 1`. Gated on the `community_events` module. Member-JWT.
Inspect the contract. Build against generated truth.
This reference reads the same typed endpoint registry that generates Booking Bible’s OpenAPI 3.1 document. Start with a group, then open only the parameters and examples you need.
Route handlers are the complete deployed /api/v1 surface. Documented operations are the partner-facing contracts currently registered for OpenAPI.
Start with the contract
Authentication and versioning are explicit
Public discovery routes need no credentials. Protected routes accept a user bearer token, an organization-scoped API key, or the method documented for that operation.
Organization-scoped credentials
Create API keys under Admin → Settings → Developer. Each key is shown once, carries explicit scopes, and remains bound to its venue.
Version pinned by header
Send X-Api-Version to pin behavior. The current documented version is 2026-04-11.
Groups and operations below are generated from the application registry. Download the OpenAPI JSON for code generation or machine-readable inspection.
Discovery20 documented operations
GET/api/v1/discoveryPublic
Discover live venue inventory
Bounded, paginated Universe discovery for one canonical world and venue-local date. Returns capped live class or service/appointment summaries with explicit partial failures; member coordinates are not accepted.
Parameters, scopes and examples
Query parameters
worldstring · required
Required: classes, treatments, or salon
datestring · required
Required venue-local date (YYYY-MM-DD)
time_windowstring
any, morning, afternoon, or eveningDefault: any
searchstring
Venue, location, class, or service search
pageinteger
Page numberDefault: 1
limitinteger
Venue items per page (max 4)Default: 4
GET/api/v1/discovery/countsPublic
Live per-world "open today" venue counts
Lightweight counterpart to /api/v1/discovery for the Explore landing screen: how many venues actually have live same-day inventory in each world today, not how many venues merely support the world. Reuses the same same-day-activity signals as discovery ordering (classes: class_instances existence; treatments/salon: the union of provider_schedules rostering and real appointments existence — a same-day proxy, not exact slot availability).
Full venue profile including brands, locations with rooms, opening hours, amenities, and photos.
Parameters, scopes and examples
Path parameters
slugstring · required
Venue URL slug
GET/api/v1/venues/{slug}/joinBearer token
Check venue join eligibility
Bearer-authenticated, read-only membership check for Consumer apps. Resolves the target from its slug or organization UUID and reports member, can_join, or an unavailable reason without changing account state.
Parameters, scopes and examples
Path parameters
slugstring · required
Venue URL slug or organization UUID
POST/api/v1/venues/{slug}/joinBearer token
Join a venue with explicit consent
Bearer-authenticated and Idempotency-Key protected. Requires {consent:true}; creates one active member relationship without changing an existing role, assigns the venue client ID, and emits the canonical audit, analytics, and member.created integration events.
Parameters, scopes and examples
Path parameters
slugstring · required
Venue URL slug or organization UUID
Explicit user consent to add this venue to their account.
Request body
{
"consent": true
}
GET/api/v1/venues/{slug}/schedulePublic
Get class schedule
Live class schedule with real-time availability. Filter by date range, location, brand, class type, instructor, or online-only. Each row includes the additive general-policy `requires_workshop_entry` flag and a nullable public `workshop_entry_target`; `is_bookable` retains its capacity/status/time meaning.
Bearer-authenticated exact class detail for Consumer push deep links. Requires X-Organization-ID for an active member relationship, retains public/member-entitled completed or cancelled classes, and never exposes an unlisted class. Includes the same additive general-policy `requires_workshop_entry` and nullable public `workshop_entry_target` fields as the venue schedule; `is_bookable` remains capacity/status/time-only.
All active pass types with pricing tiers, binding commitments, class restrictions, and location availability. Returns an `{ org, pass_types }` envelope (BB-R4): `org` carries `slug`, `name`, `currency`, `timezone`, and `vat_exempt_age_threshold` (for an under-/over-threshold pricing toggle); each `pass_types` entry includes its `slug` for `/buy/{slug}` deep-links. Configurable recurring entries also include `pricing_mode`, billing cadence, the immutable active pricing version, quantity range/step, volume tiers, unlimited option, and change-cycle policy. Optional `category`, `location_id`, `brand_id` filters apply to `pass_types`.
Class catalog with descriptions, difficulty levels, durations, and included services.
Parameters, scopes and examples
Required scopes
read:classes
Path parameters
slugstring · required
Venue URL slug
GET/api/v1/venues/{slug}/instructorsPublic
Get instructors
Instructor profiles with bios, photos, and specialties.
Parameters, scopes and examples
Required scopes
read:instructors
Path parameters
slugstring · required
Venue URL slug
GET/api/v1/venues/{slug}/locationsPublic
Get locations
Physical locations with rooms, capacity, opening hours, amenities, and Google Maps integration.
Parameters, scopes and examples
Required scopes
read:locations
Path parameters
slugstring · required
Venue URL slug
GET/api/v1/venues/{slug}/brandsPublic
List brands
Active brands at this venue. Each entry includes identity (name, slug, description), theming (colors, logo, hero), social links, and a class_types_count for quick summary rendering.
Parameters, scopes and examples
Required scopes
read:brands
Path parameters
slugstring · required
Venue URL slug
GET/api/v1/venues/{slug}/brands/{brandSlug}Public
Get brand detail
Full brand record plus class_types tagged to this brand and the pass_types available for it (respecting pass_type_brands restrictions — passes with no brand-junction rows are venue-wide and are included).
Parameters, scopes and examples
Required scopes
read:brands
Path parameters
slugstring · required
Venue URL slug
brandSlugstring · required
Brand slug within the venue
GET/api/v1/geoPublic
Geo prefill for the signup form
Anon utility that reads Vercel's request-geo headers (`x-vercel-ip-country`/`x-vercel-ip-city`) so a client can prefill signup's optional `country`/`city` fields from the caller's own IP before submitting POST /api/v1/auth/signup. `country` is an ISO 3166-1 alpha-2 code; `city` is URI-decoded free text. Either is `null` when the header is absent (e.g. local dev). No DB touch; never cached (per-caller response).
PUBLIC (no auth) balance lookup for a venue gift card, for a storefront "check your balance" widget. Org resolved from {slug}; lookup scoped to that org's cards by the FULL generated code OR the printed physical barcode (same fallback `redeemGiftCard` uses). Returns the minimal `{ code, remaining_amount, currency, status, expires_at }` — never purchaser/recipient PII. Enumeration-hardened: an unknown code, a cross-org code under the wrong slug, and a cancelled card all return the SAME generic 404. IP rate-limited (20/min).
Parameters, scopes and examples
Path parameters
slugstring · required
Venue URL slug
codestring · required
Full gift-card code or the printed physical barcode
PUBLIC (no auth) gift-card preview so a BRANDED storefront can show a recipient what they were gifted ("Alex sent you a 3-month membership") before prompting signup/redeem — instead of bouncing them to BB's /gift/redeem/[code] venue portal. Accepts the generated code OR the printed physical barcode. Returns `{ code, gift_type, sender_name, gift_description, pass_name, amount, currency, status, expires_at }` — the sender's display name + a human gift description only, NEVER recipient/purchaser contact info, the personal message, or the redeemer. Enumeration-hardened: unknown code, wrong slug, and a cancelled card all return the SAME generic 404. IP rate-limited (20/min). Redeem itself is member-authenticated (POST /api/v1/gift-cards/redeem).
Parameters, scopes and examples
Path parameters
slugstring · required
Venue URL slug
codestring · required
Full gift-card code or the printed physical barcode
Member-authenticated request-a-booking for a service that accepts inquiries (`accepts_inquiries: true` on the public services list). Free-text preferred time, not a real slot — the venue converts it to a real appointment once a time is agreed. `Idempotency-Key` is optional but MUST be a UUID when sent (400 `INVALID_IDEMPOTENCY_KEY` otherwise); it is the database ingest request id. Every 201 and every 503 `INQUIRY_RECONCILE_FAILED` returns an `Idempotency-Key` RESPONSE header (mirrored as `error.details.request_id` on the 503) carrying the identity the inquiry was accepted under — your key when you sent one, the server-generated UUID when you did not. Retry a 503 with that exact value as `Idempotency-Key`: it replays the accepted inquiry (no second row) and re-drives only what is still missing. Retrying without it mints a new identity and files a duplicate inquiry. Rate-limited 10/min. Errors: 503 INQUIRIES_DISABLED (kill switch off), 404 NOT_FOUND (venue), 422 SERVICE_NOT_ACCEPTING_INQUIRIES, 422 VALIDATION_FAILED, 409 IDEMPOTENCY_KEY_REUSE_MISMATCH (same key, different answers — nothing written), 500 SUBMIT_FAILED (`details.reason` passthrough).
Parameters, scopes and examples
Path parameters
slugstring · required
Venue URL slug
Inquiry details
Request body
{
"service_id": "uuid",
"preferred_time": "Tuesdays or Thursdays after 17:00",
"message": "Looking for a 90-minute deep tissue session.",
"contact_name": "Jane Doe",
"contact_email": "jane@example.com",
"contact_phone": "+4520123456"
}
Response example
{
"data": {
"id": "uuid"
},
"error": null
}
GET/api/v1/me/inquiriesBearer token
My booking inquiries
The caller's own booking inquiries across every venue, newest first. Status is mapped to plain language (never the raw form_submissions enum): 'Sent — waiting for the venue', 'The venue replied', or 'Closed'; archived/spam/deleted rows are never returned.
Exchange a Google or Apple identity token for a Booking Bible session. Existing email/password accounts are unchanged. New accounts require a venue (organization_id or organization_slug). Unclaimed imported emails are not auto-linked.
Exchange a device-bound mobile token_hash or a 6-digit email OTP for a session. Mobile token hashes require the callback request_id and device-held code_verifier. Rate-limited 5/10 min per IP.
Check whether an email already has an account before signup. Returns a state hint (absent | active | password_never_used | imported_unclaimed | unknown). Rate-limited 20/min per IP. Constant-time floor of 250ms to prevent enumeration.
Create a TOTP challenge for one of the caller's factors. Returns {id, expires_at}; pass the id as challenge_id to /auth/mfa/verify. Repeating mints a fresh challenge (intended resend). Rate-limited 5/min per user.
Verify a 6-digit TOTP code (enrollment confirmation or login challenge). Accepts {type: totp, factor_id, code}, {type: backup_code, code}, or the challenge-bound {challenge_id, code} (factor resolved from the preceding /mfa/challenge). Rate-limited 5/5 min per user.
Generate 10 single-use backup codes for MFA recovery. Previous unused codes are invalidated. Codes are shown once in plaintext — only hashes are stored.
Parameters, scopes and examples
Response example
{
"data": {
"backup_codes": [
"ABCD1234EF",
"..."
],
"warning": "Save these codes securely. They will not be shown again.",
"count": 10
}
}
POST/api/v1/auth/mfa/resetBearer or API key
Admin MFA reset
Admin-initiated MFA reset for a user. Unenrolls all factors and invalidates backup codes. Permission: admin.users.manage.
Exchange refresh token for new access and refresh tokens.
Parameters, scopes and examples
Refresh token
Request body
{
"refresh_token": "xxx"
}
POST/api/v1/auth/logoutBearer token
Log out current session
Revoke the refreshable Supabase session represented by the caller JWT. The access JWT remains valid until its encoded expiry.
Parameters, scopes and examples
Response example
{
"data": {
"ok": true
}
}
POST/api/v1/auth/password/forgotPublic
Forgot password
Send a single-use 6-digit password verification code. Honours venue branding when an org is identified and never reveals whether the email exists. No reset link is generated.
Authenticated password change that still requires a fresh single-use numeric code. Submit the code and new password together; current-password-only and session-only changes are rejected.
NAMASTE-GATES-01 — mint a one-time SSO handoff code (60s TTL, single-use, SHA-256 hashed at rest) bound to an allowlisted destination domain. The destination site exchanges it at /auth/handoff-exchange for a fresh session.
NAMASTE-GATES-01 — consume a one-time handoff code (atomic single-use) and receive a fresh Supabase session for the bound user. Same session shape as /auth/login.
TV-DEVICE-AUTH-01 — RFC 8628-style device authorization (mint side). An input-constrained device (TV) receives a 256-bit device_code (its poll credential) plus a short user_code (shown as XXXX-XXXX + QR). Both are SHA-256 hashed at rest, bound to one 10-minute expiry, single-use.
TV-DEVICE-AUTH-01 — a SIGNED-IN member submits the user_code shown on the TV (normalized: uppercase, dashes/spaces stripped). Binds the pending device code to the caller so the TV poll returns a session. Every failure (unknown / expired / attempts-capped) is the same generic 400 INVALID_CODE; per-code attempts<5 cap.
Parameters, scopes and examples
The short code shown on the TV
Request body
{
"user_code": "ABCD-EFGH"
}
Response example
{
"data": {
"approved": true
}
}
POST/api/v1/auth/device/tokenPublic
Poll TV device code for session
TV-DEVICE-AUTH-01 — the TV polls with its device_code (every `interval` seconds). 400 AUTHORIZATION_PENDING until approved; 400 EXPIRED_TOKEN / 403 ACCESS_DENIED / 400 INVALID_CODE are terminal. On approval the code is consumed atomically (single-use) and a fresh Supabase session is returned — same shape as /auth/login.
Venue-local YYYY-MM-DD start of a pass usage cycle (inclusive). Requires organization_id.
cycle_endstring
Venue-local YYYY-MM-DD end of a pass usage cycle (exclusive). Requires organization_id.
afterstring
Cursor for pagination
limitinteger
Items per pageDefault: 20
POST/api/v1/bookingsBearer or API key
Book a class
Create a booking. Validates pass eligibility, capacity, booking window, daily limits, and class restrictions. Supports idempotency via Idempotency-Key header.
Retrieve a single booking with class details, pass info, and check-in status.
Parameters, scopes and examples
Required scopes
read:bookings
Path parameters
idstring · required
Booking ID
POST/api/v1/bookings/buddyBearer token
Invite a client to the same class
Creates and emails a venue-branded invitation to an existing active client at the same venue. The inviter must already have a confirmed booking. The recipient books with their own pass or payment; no guest funding or inviter entitlement is used. Idempotency-Key supported.
Accepts a buddy invitation for the authenticated recipient and books the same class with that recipient’s own eligible pass or normal venue booking rules. The invited email/account and active venue membership must match. Idempotency-Key supported.
NAMASTE-GATES-01 / LIVE-PARTICIPATION-01 — entitlement-based live-watch path. Requires an active pass whose type grants online class access and covers the class type; finds or creates the caller’s attendance_type=online booking (idempotent, respects online_capacity, consumes a clip only for clip-based passes) and returns a signed Mux playback URL plus viewer-session telemetry token. First admission opens 10 minutes before start and closes exactly at start; an admitted viewer may recover through end + 5 minutes unless they explicitly leave after start.
Client-facing: exchange a valid QR token for a check-in on the caller's booking. Token must be active and not expired. Anti-replay: a booking can only transition to checked_in once. Every scan writes an audit_log entry regardless of outcome.
Returns the authenticated member’s frozen selection, current published recurring choices, pricing version, and any pending renewal change. Owner-scoped and available only when member changes are enabled by the venue.
Preview a venue-approved quantity/unlimited change for renewal 1–24, or schedule it with the exact unexpired quote fingerprint. The change takes effect only after the target renewal invoice is paid.
Cancels the authenticated member’s pending future allowance change without altering the current frozen entitlement.
Parameters, scopes and examples
Path parameters
idstring · required
Issued pass UUID
GET/api/v1/pass-types/{id}Public
Pass-type catalog detail
Pass-type catalog detail used by checkout — price, duration, benefits, binding tiers, eligible class types, configurable kind, and the immutable published Flexible pass configuration when enabled. Active public items need no authentication; a hidden exhausted-credit target requires member JWT authentication plus its clips_empty_offer_id capability.
Parameters, scopes and examples
Path parameters
idstring · required
Pass-type id
Query parameters
clips_empty_offer_idstring
Opaque clips-empty path UUID. Revalidated against the authenticated member’s exhausted, still-valid source pass before a hidden target is returned.
Initiate pass purchase. Returns a PaymentIntent or SetupIntent client_secret (`client_secret_type` identifies which) with the provider-frozen customer, ephemeral key, Connect account, merchant country, and regional revision. Customer credentials are paired and may both be null only for a supported generic-sheet/no-customer result. Optional binding_months must identify a current server-side tier and is priced by the same canonical resolver as checkout preview; unavailable tiers return 422 rather than falling back. Flexible recurring passes require selection_kind=quantity with quantity, or selection_kind=unlimited; Flexible class/time passes require selection_kind=option with option_id. A recurring Flexible pass accepts only a Flash Sale backed by one shared introductory amount, which applies regardless of the chosen allowance. Fixed passes retain promo codes, gift cards, and account credits.
MEMBER (JWT) redeem endpoint so a branded storefront can host the whole redeem flow on its own domain. Applies the gift `{ code }` — generated code OR printed physical barcode — to the caller's account: a custom-amount gift credits the balance (`{ type:"credit", amount, newBalance }`); a pass gift creates + activates a pass (`{ type:"pass", passId }`). Atomic SELECT FOR UPDATE claim — two concurrent calls can never both redeem. A logged-out recipient must sign up / log in first (that creates/links the BB member); this endpoint is member-only by design. Org from the caller's active membership (X-Organization-ID header or single membership). Errors: 401 UNAUTHORIZED, 403 NO_ORG / MODULE_DISABLED, 400 VALIDATION_ERROR, 404 INVALID_CODE, 409 ALREADY_REDEEMED / EXPIRED / NOT_AVAILABLE, 500 REDEEM_FAILED.
Parameters, scopes and examples
Gift card code to redeem onto the caller's account
Gift cards the caller purchased or received (buyer, redeemer, or addressed recipient email), scoped to the active org. Returns `{id, code, initial_amount, balance, currency, status, recipient_email, recipient_name, message, expires_at, created_at}` with status `active|redeemed|expired|void`. Degrades to an empty list when the gift_cards module is off or no org context resolves.
POST/api/v1/gift-cards/purchaseBearer token
Purchase gift card
Buy a gift card (custom amount or a gifted pass) for a recipient. Creates a Stripe one-time PaymentIntent and returns client_secret plus customer_id + ephemeral_key for the Stripe Payment Sheet. Gated on the gift_cards module. Idempotency-Key supported.
Parameters, scopes and examples
Gift card purchase
Request body
{
"gift_type": "custom_amount",
"organization_id": "uuid",
"amount": 500,
"sender_name": "Alex",
"recipient_name": "Jordan",
"recipient_email": "jordan@example.com",
"delivery_method": "email",
"personal_message": "Enjoy a class on me!"
}
Anonymous (or logged-in) native gift-card checkout — phase 1. Mints a Stripe PaymentIntent for a gift card and returns client_secret so the buyer can mount Stripe Elements in a modal. Two gift kinds (exactly one of the two fields): a CUSTOM-AMOUNT gift via `amount` (smallest currency unit, min 5000, max 5000000), or a PASS-BASED gift via `pass_type_id` (GIFT-PASS-API-01 — must be an active, giftable, non-intro pass type of this org; price is server-resolved via calculateGiftPrice, optional `duration_months` 1–120 prepays a recurring membership). The gift_cards row is created only on confirm, so an abandoned payment leaves no orphan. Anonymous callers must pass a Turnstile token. VAT is accounted at redemption (multi-purpose voucher) so vat_amount is 0. Gated on the gift_cards module. Rate-limited 10/min.
Parameters, scopes and examples
Gift card checkout — custom-amount example; for a pass gift replace `amount` with `pass_type_id` (+ optional `duration_months`)
Request body
{
"organization_slug": "hot-yoga-cph",
"amount": 50000,
"recipient_email": "jordan@example.com",
"recipient_name": "Jordan",
"sender_name": "Alex",
"message": "Enjoy a class on me!",
"giver_email": "alex@example.com"
}
Native gift-card checkout — phase 2. After the buyer confirms the PaymentIntent client-side, this creates the gift_cards row (custom-amount OR pass-based — the pass identity rides in PI metadata), delivers it to the recipient, and emails the giver a receipt. Idempotent on the PaymentIntent — a retry or the webhook backstop never double-creates a card. Returns the last 4 of the code, a masked recipient email, and gift_type. organization_slug is optional but recommended for direct-charge venues.
Pause (freeze) a pass for a date range. Owner-scoped. Enforces the pass-type pause rules (allow_pause, min/max duration, annual freeze budget) + binding gate, pauses Stripe billing, and bills any pause fee. Idempotency-Key supported.
Move a deferred (pending_activation) membership start to today or an earlier future date: re-anchors Stripe billing, charges the first membership payment, and activates the pass. Owner-scoped. Idempotency-Key supported; rate-limited 5/min. Returns payment_status succeeded | requires_action (confirm with client_secret; the invoice.paid path then activates) | pending. Errors: PASS_NOT_FOUND, FORBIDDEN, ALREADY_STARTED, IN_PROGRESS, INVALID_START_DATE, PAYMENT_FAILED, STRIPE_UNAVAILABLE.
Parameters, scopes and examples
Path parameters
idstring · required
Pass id
New start date (must be earlier than the current start)
Owner-scoped, read-only termination summary resolved from the venue self-cancel gate, binding period, termination policy, Stripe billing boundary and remaining class balance. Returns can_confirm=false with the blocking reason while a binding period is active.
Parameters, scopes and examples
Path parameters
idstring · required
Pass id
POST/api/v1/me/passes/{id}/terminateBearer token
Terminate a recurring membership
Confirmed owner-scoped membership termination. Enforces venue allow_member_cancel, minimum membership age, binding period, required reason and the configured termination boundary. Stripe synchronization is fail-closed and Idempotency-Key is supported.
Parameters, scopes and examples
Path parameters
idstring · required
Pass id
Explicit acknowledgement and optional/venue-required reason
Request body
{
"acknowledged": true,
"reason": "Moving away"
}
GET/api/v1/me/passes/{id}/extensionBearer token
Get pass extension quote
Return the authenticated member’s venue-scoped self-extension policy and live quote: proposed expiry, price/currency, configured duration, remaining extension allowance, clips, and a machine-readable unavailable_reason. Requires X-Organization-ID and fails closed on invalid venue configuration.
Revalidates the venue’s live self-extension policy and creates a durable operation before any processor call. Paid responses include PaymentSheet customer/ephemeral-key credentials in the exact frozen Stripe namespace; direct mode returns stripe_account_id. Free responses still return operation_id but do not mutate the pass. Requires X-Organization-ID and Idempotency-Key.
Authoritatively rechecks owner, tenant, venue policy, maximum count, hard end, frozen Stripe provenance and payment status under database locks. Paid success atomically records payment, fee, audit, pass, and operation; explicit post-charge conflicts are idempotently refunded. Nonterminal 202 statuses are finalizing or refund_pending and are safe to retry. The Stripe webhook shares this reconciler. Idempotency-Key is required.
Parameters, scopes and examples
Path parameters
idstring · required
Pass id
Durable operation reference plus PI reference for paid extensions
Request body
{
"operation_id": "uuid",
"payment_intent_id": "pi_xxx (omit when free)"
}
Accept a pending pass-share invitation by token. Verifies the caller’s email matches the invite recipient, then grants booking access by appending the caller to `passes.shared_with` (respecting `pass_types.max_sharers`) and converges the share into the `pass_shares` table. Emits `pass.share_accepted`. Member-JWT. Idempotency-Key supported.
Published VODs and class replays for the caller's venue. Visibility public + members only; pass_restricted items are accessible via /video-catalog/:id once the pass check passes. Signed Mux playback URLs valid for 2 hours.
Parameters, scopes and examples
Query parameters
pageinteger
Page numberDefault: 1
limitinteger
Items per page (max 50)Default: 20
categorystring
Filter by category (class_recording | tutorial | workshop)
The caller's required/pending intake forms for their active org. Each entry is annotated with whether the member already submitted (the pre-booking form gate's source of truth). Returns [] when the `forms` module is disabled.
Parameters, scopes and examples
Response example
{
"data": [
{
"id": "uuid",
"slug": "new-client-intake",
"name": "New Client Intake",
"description": "Tell us about your practice and any injuries.",
"required": true,
"submitted": false,
"submission_id": null,
"submitted_at": null
}
]
}
GET/api/v1/forms/{id}Bearer token
Get form schema
Render schema (fields, steps, submit label) plus the venue's configured `legal_basis` (`consent` | `contract` | `legitimate_interest` | `legal_obligation`) for a single published form. Use `legal_basis` to render the matching privacy notice and, for a `consent` form, to present its required consent checkbox as the gate it is — a consent-basis submission is refused unless that box was ticked. Scoped to the active org — forms in other orgs return 404.
Parameters, scopes and examples
Path parameters
idstring · required
Form UUID
Response example
{
"data": {
"id": "uuid",
"slug": "new-client-intake",
"name": "New Client Intake",
"description": "Tell us about your practice and any injuries.",
"required": true,
"legal_basis": "consent",
"schema": {
"version": 1,
"fields": [],
"steps": null,
"submit_label": "Submit"
},
"thank_you": {}
}
}
POST/api/v1/forms/{id}/submitBearer token
Submit a form
Submit `{ answers }` for a published form. Validates required fields + types, persists a submission stamped with the caller, and routes it into the unified inbox. `Idempotency-Key` is optional but MUST be a UUID when sent (400 `INVALID_IDEMPOTENCY_KEY` otherwise) — it is both the HTTP replay token and the database ingest request id. The same key with the same answers replays the original response; the same key with different answers writes nothing and returns 409 `IDEMPOTENCY_KEY_REUSE_MISMATCH`, so mint a new key whenever the answers change. Every 201 and every 503 `SUBMIT_RECONCILE_FAILED` returns an `Idempotency-Key` RESPONSE header (mirrored as `error.details.request_id` on the 503) carrying the identity the submission was accepted under — your key when you sent one, the server-generated UUID when you did not. Retry a 503 with that exact value as `Idempotency-Key`: it replays the accepted submission and re-drives only the missing delivery. Retrying without it mints a new identity and files a duplicate. 422 with `details.missing[]` on required-field failures; 422 `CONSENT_REQUIRED` when a consent-basis form was sent without its consent box ticked; 409 `FORM_CONSENT_MISCONFIGURED` when the form itself cannot lawfully collect.
The caller's referral status for their active org: code, referred-friend count, conversions, rewards earned, and an anonymized (first-name + last-initial) per-referral list. Returns an empty summary when the `referrals` module is disabled.
Active public venues ranked by explicit favourite, then canonical pass, class-booking, and appointment signals. Returns counts and last activity; caller identity is server-bound.
Idempotently removes only the authenticated member and requested venue pair.
Parameters, scopes and examples
Path parameters
organizationIdstring · required
Venue organization UUID
GET/api/v1/me/credits/balancesBearer token
My venue credit balances
Complete ledger-derived balances grouped by venue and currency. Each row carries `balance` (the venue ledger total), `available` (that total minus credit held by open checkout reservations, which is what checkout will actually spend) and `reserved`. Amounts are in major units. Consumer is account-wide; branded requests are fail-closed to x-organization-slug.
POST/api/v1/me/avatar/upload-urlBearer token
Create avatar upload ticket
Returns a caller-owned, MIME-bound storage path and two-hour signed upload URL for PNG, JPEG, or WebP up to 5 MB.
PATCH/api/v1/me/avatarBearer token
Finalize my avatar
Validates caller path ownership, metadata, size, and image magic bytes before deriving and saving the public URL.
DELETE/api/v1/me/avatarBearer token
Remove my avatar
Idempotently clears the profile reference and removes only the caller-owned canonical avatar object.
GET/api/v1/me/workspace-profileBearer token
My active venue operating profile
Server-authoritative Business-app profile for the active venue selected by X-Organization-ID. Returns booking_mode (classes, appointments, or both), business_type, resolved class/appointment operation gates, venue surface applicability, appointment access/counts, active_modules, and the resolved vertical_modules visibility map. business_type is informational and never used to infer booking_mode. Surface values are venue-level applicability; clients must still intersect them with the caller's effective permissions from GET /api/v1/me.
Current user profile with all active venue memberships and roles. Each membership carries `permissions: string[]` (the caller's OWN effective permission keys for that org — per-user overrides applied over role/capability defaults, resolved identically to requireApiPermissionWithDefaults) and `capabilities: string[]` (the membership capability set, surfaced for every membership). To bound per-request cost in this multi-tenant app, `permissions` is resolved for the ACTIVE org only (top-level `permissions_scope: "active_org"`; non-active memberships carry `[]`) — mobile refetches /me on org switch. Workspace ownership is server-projected as `is_individual`, `is_owned`, `is_workplace`, `is_relationship`, `is_selectable`, and an explicit `workspace_group` (`owned`, `works_at`, `member_venues`, or `relationships`). Accepted role-bearing employer memberships remain selectable in Business under “Works at”; member-only Network relationships do not. Business clients must only put selectable rows in their workspace picker. Gates UI on these instead of discovering denials via 403s. A PATCH /admin/permissions/user/{userId} is reflected within ≤60s (permission-cache TTL). Caller's own permissions only. See docs/api/ME_PERMISSIONS_CONTRACT.md.
POST/api/v1/me/active-organizationBearer token
Switch my active workspace
Authoritatively switches the caller to an active, selectable workspace. When the caller owns an individual professional venue, accepted role-bearing employer memberships remain selectable; only non-operational/member-only relationships return WORKSPACE_NOT_SELECTABLE. The response includes effective permissions for the selected workspace so native role gating is safe immediately.
Resolved feature-module map for the caller's active org (C07): `{ <module_key>: { enabled, source, tier?, settings? } }` — the same four-tier resolution (plan → group → venue → tenant) the admin sees at /admin/features. Also includes `professional_collaborations`, which reflects the platform-wide teacher-settlements rollout independently of the venue-to-venue `network` plan gate. Drives every <FeatureGate> in the branded mobile app. Multi-membership callers must send X-Organization-ID; without it the map resolves empty (all off).
GET/api/v1/me/minimalPublic
Minimal auth check
Cross-origin auth check for venue marketing sites. Returns { logged_in, first_name, venue_id, preferred_brand_id } — or logged_in=false when no session. CORS is gated by the venue/brand embed_allowed_origins allowlist; unknown origins get no CORS headers (treated as "not logged in" by the caller).
GET/api/v1/me/entitlementsBearer token
My entitlements
NAMASTE-GATES-01 — the caller's entitlement matrix for one venue: can_book_physical (any active pass with grants_in_person), can_watch_online (grants_online_class_access), online_only, bookable_class_type_ids ("all" when any usable pass is unrestricted), and an active_passes[] summary (slug, category, grants, validity, clips). The create_booking RPC enforces the same matrix atomically.
Create an account-local Stripe SetupIntent plus matching Customer/ephemeral-key credentials. Requires an Idempotency-Key header. The response freezes the server-owned venue country and exact Connect account for native Payment Sheet initialization.
Member self-service cannot detach saved cards. This endpoint returns PAYMENT_METHOD_REMOVAL_NOT_ALLOWED; add a replacement card or contact venue staff instead.
Promote a saved card to the Stripe customer default (invoice_settings.default_payment_method). Empty body, idempotent via Idempotency-Key. GET /me/payment-methods then returns is_default:true on the matching row (PAY-P3.1).
Parameters, scopes and examples
Path parameters
idstring · required
Stripe payment method ID
POST/api/v1/me/payment-sheet-initBearer token
Initialise Payment Sheet
Setup-only flow for Stripe Payment Sheet (PAY-P1.1). Requires an Idempotency-Key header. Returns customer_id, ephemeral_key, setup_intent_client_secret, and apple_merchant_id in the exact SetupIntent home account: connected only in direct mode, otherwise platform. Use when collecting a saved card before any purchase.
Register an Expo push notification token for iOS/Android/web. app_variant is required so member, branded-venue, and staff deliveries cannot cross application boundaries. Branded and business tokens also require a validated organization context.
Deactivate the authenticated user's token or device before logout. The token/device selector is sent in the JSON body.
Parameters, scopes and examples
At least one token or device_id is required
Request body
{
"device_id": "installation-uuid"
}
GET/api/v1/me/notificationsBearer token
Notification history
Cursor/page-paginated email, SMS, push, and in-app history. Rows include source-aware `data`, `read_at`, and `app_variant`; X-App-Variant filters app-specific inbox events, while X-Organization-Slug narrows branded clients to their venue.
Self-scoped read marker. Idempotency-Key is required; another user’s row returns 404.
Parameters, scopes and examples
Path parameters
idstring · required
Notification id
POST/api/v1/me/notifications/read-allBearer token
Mark notifications read
Marks all of the caller’s unread rows read. X-Organization-Slug narrows a branded client to its exact venue; otherwise Consumer marks its cross-venue inbox. Idempotency-Key is required.
Returns the canonical ten-category catalog with effective email/SMS/push defaults, frequency caps, and per-member quiet hours for the active/requested organization.
Upserts canonical category toggles/frequency caps and quiet hours. Unknown categories are rejected and every database failure is returned; Idempotency-Key is required.
GET/api/v1/me/paymentsBearer token
List my payments
Cursor-paginated receipt-bearing payment ledger for the caller. Pending and failed attempts are excluded; successful, refunded, partially-refunded and disputed originals remain available with their payment receipt.
Parameters, scopes and examples
Query parameters
limitnumber
Page size (default 20, max 100)
afterstring
Opaque cursor from a previous page
GET/api/v1/me/invoicesBearer token
List my invoices
Cursor-paginated list of the caller's member-visible client invoices. Drafts are excluded and every row includes an authenticated document_path for the print-ready HTML invoice.
Parameters, scopes and examples
Query parameters
limitnumber
Page size (default 20, max 100)
afterstring
Opaque cursor from a previous page
GET/api/v1/me/invoices/{id}Bearer token
Invoice detail
Owner-scoped detail for one issued client invoice, including line items and the totals breakdown (subtotal, discount, VAT, total, amount_paid).
Streams the receipt PDF (application/pdf) for one of the caller's payments — branded merchant header, line items, VAT breakdown, totals. Cached in storage after first render.
Emails the venue-branded receipt PDF for one of the caller's own payments to the address already on file for their account — the same document served by the PDF download. No recipient field exists; any caller-supplied recipient is ignored. Idempotency-Key is required; a retried key replays the cached result instead of re-sending.
Parameters, scopes and examples
Path parameters
paymentIdstring · required
Payment id
Empty body — the request is never read.
Request body
{}
GET/api/v1/me/invoices/{id}/documentBearer token
Invoice print document
Authenticated owner- and venue-scoped print-ready HTML for one member-visible invoice.
Parameters, scopes and examples
Path parameters
idstring · required
Invoice id
GET/api/v1/me/refundsBearer token
List member refund receipts
Owner-scoped successful refund operations, cursor-paginated and optionally restricted by the branded organization slug. Split-tender operations are returned once with a signed negative amount.
Parameters, scopes and examples
Query parameters
limitnumber
Page size (default 20, max 100)
afterstring
Opaque cursor from a previous page
GET/api/v1/me/refunds/{refundId}/pdfBearer token
Refund receipt PDF
Owner- and venue-scoped canonical refund receipt PDF. Non-final, sibling, cross-member and cross-venue refund ids return a uniform not-found response.
Parameters, scopes and examples
Path parameters
refundIdstring · required
Refund id
GET/api/v1/me/loyaltyBearer token
Loyalty balance + history
The caller's org-scoped loyalty point balance plus a recent per-event history slice. Full paginated history is on /api/v1/me/loyalty/points.
GET/api/v1/me/loyalty/pointsBearer token
Loyalty points history
Cursor-paginated per-event loyalty point ledger for the caller.
GET/api/v1/me/streakBearer token
Attendance streak
Current + longest attendance streak, freezes remaining, and at-risk flag.
GET/api/v1/me/rewardsBearer token
Redeemable rewards catalog
Active loyalty rewards for the caller's org with affordability (is_locked) computed against the caller's balance.
POST/api/v1/me/rewards/redeemBearer token
Redeem a reward
Redeem a loyalty reward. Idempotency-Key supported; audited.
Parameters, scopes and examples
Redemption
Request body
{
"reward_id": "uuid"
}
POST/api/v1/feedbackBearer token
Submit feedback & tip
Rate a class (1-5 stars), leave a comment (optionally `anonymous`), and optionally tip the instructor via Stripe. The tip carries its own `anonymous` flag. The tip block of the response returns `client_secret`, `customer_id`, `ephemeral_key`, and `stripe_account_id` (non-null only in DIRECT charge mode).
Self-scoped class/appointment review and tip eligibility. Organization, target, settings, MobilePay capability, and prompt decision are server-derived from the owned source. Reads are side-effect-free unless `claim_prompt=true` is explicitly supplied by a prompt-mode entry check.
Parameters, scopes and examples
Query parameters
source_typestring · required
class or appointment
source_idstring · required
Owned booking id (class) or appointment id
claim_promptboolean
Reserve an in-app prompt only when true
POST/api/v1/post-attendance/reviewsBearer token
Submit class or appointment review
Creates one source-aware review after server-authoritative attendance/settings checks. Idempotency-Key required. `professional_rating`, tags, recommendation, anonymity, moderation, recipient notification, analytics, and webhooks are venue-controlled.
POST/api/v1/tipsBearer token
Tip a professional (no review)
Create a class or appointment tip in major currency units (`amount: 20` means DKK 20). Organization, professional, currency, Stripe account, and available methods are server-derived. Customer + ephemeral key are optional: customerless PaymentSheet still supports adding a card. MobilePay is returned only for verified Danish/DKK/venue-capable configurations. Idempotency-Key required.
Authenticated tipper-only reconciliation after PaymentSheet/MobilePay/3DS returns. Retrieves the server-owned PaymentIntent in its frozen Stripe account namespace, validates amount/currency/metadata, and emits receipts only after Stripe reports succeeded. Idempotency-Key required.
Parameters, scopes and examples
Path parameters
idstring · required
Tip id
GET/api/v1/tips/{id}Bearer or API key
Tip status
Poll a tip's status after confirming its PaymentIntent (incl. MobilePay / 3DS redirect returns). Access: the tipper (JWT), an org admin/manager (JWT), or an org-scoped API key. Cross-user / cross-tenant reads return 404.
Submit an Art. 15/16/17/20/21/22 request (access, erasure, portability, rectification, objection, art22 review). 30-day SLA. For erasure, account access is disabled immediately and the response reports erasure_status=pending_fulfillment; a super-admin performs the guarded erasure cascade within the SLA, while the SLA cron only alerts. Statutory records may be anonymised and retained for their legal period. Idempotency-Key required.
Parameters, scopes and examples
DSR request
Request body
{
"kind": "access",
"details": "Please send all data you have on me."
}
Re-trigger the guardian verification email for the caller's outstanding parental-consent request (C06). Matched by the authenticated email — no enumeration. Rotates the token and refreshes the 7-day expiry on the existing pending row (never a duplicate request). Empty body; Idempotency-Key supported; throttled 3/min per IP + 5/hr per user.
GET/api/v1/me/consent-statusBearer token
Active consents
Latest consent record per consent_type for the authenticated user (TOS, marketing, cookies, AI, etc). is_active is fail-closed and true only when the grant is unwithdrawn and policy_version matches the server-canonical current_policy_version; stale grants return requires_reacceptance=true.
GET/api/v1/me/consentBearer token
Current native consent state + venue requirement
The venue's photo/video consent requirement (when organization_id is given) plus the caller's version-aware state for legal, marketing, analytics, and photo/community consent types. A stale policy version is inactive and requires reacceptance.
Parameters, scopes and examples
Query parameters
organization_idstring
Resolve the venue photo-consent requirement
POST/api/v1/me/consentBearer token
Capture native consent
Grant or withdraw one supported legal, marketing, analytics, or photo/community consent for the caller. Grant versions are resolved exclusively from the server-canonical active policy; legacy client policy_version values are accepted but ignored, and an unavailable policy returns 503 without writing. Delegates to the canonical consent record/withdraw pipeline (audit + webhook).
The caller's health-questionnaire completion timestamp (completed_at, null when never submitted). Pre-check for the mobile hot-yoga booking gate.
POST/api/v1/me/health-questionnaireBearer token
Submit health questionnaire (Art. 9)
Submit the spa/hot-yoga health questionnaire for the caller's active org. Runs the Art. 9 contraindication consent gate, inserts a health_questionnaires row (plaintext responses; encrypted at rest by cron), stamps profiles.health_questionnaire_completed_at so the booking gate clears, and writes audit_log/user_events. Requires an Idempotency-Key (a double submit replays). Org resolved via X-Organization-ID / active membership.
Read the caller's external calendar-feed state: { token, enabled, generatedAt }. token is the opaque secret embedded in the public .ics feed URL (null when no feed is provisioned).
Enable the caller's external calendar feed and return the token. Idempotent — an existing token is returned unchanged (never rotated); a new one is minted (256-bit, base64url) only when absent. Empty body. Audited (calendar_feed_token_generated).
Revoke the caller's calendar feed: clears the token and disables the feed (the public feed then 404s). Empty body. Audited (calendar_feed_token_revoked).
Parameters, scopes and examples
Response example
{
"data": {
"enabled": false
},
"error": null
}
GET/api/public/calendar-feed/{token}Public
Public calendar feed
UNAUTHENTICATED — the opaque token in the path IS the credential. Returns one user's bookings as JSON for an external calendar subscription: { bookings, cancellations, userId, generatedAt }. bookings are upcoming events for the next 90 days; cancellations are bookings cancelled in the last 7 days (so calendar apps emit STATUS:CANCELLED). 404s on an unknown or disabled token (indistinguishable). Scoped strictly to the token's single user — no other user's data. 60 req/min per token.
Buyer-facing canonical PricingBreakdown for a pass type — net/VAT split, registration fee, total today + recurring, localized policy terms, the start-date window, the required legal artifacts (with already_signed), and the buyer's saved signatures. NO charge. Member-JWT + stable x-organization-id (preferred) or legacy x-organization-slug, both membership-scoped. Query: pass_type_slug (required), start_date, binding_months, locale (en|da). A selected binding tier is validated and priced server-side; unavailable tiers return 422.
POST/api/v1/me/checkout/sign-artifactBearer token
Sign a purchase-time legal artifact
Records a waiver / ToS / privacy / contract acceptance with IP + user-agent + version + signature. Idempotent on (user, document, version); a stale version → 409 force-refetch; a minor (DOB < 18) → 409 + parental consent. Supports saved-signature reuse (saved_signature_id) honouring signature_kind. Linked contracts require pass_type_slug and may include start_date; the endpoint idempotently creates/adopts the exact current-version pre-purchase contract before signing.
GUEST-INVITE-01 — whether the caller's passes qualify them to host a guest at this class, the venue guest price, the standard single-class price to strike through (compare_at_price, display only), and any invitations they already have open for it. `reason` is plain-language copy safe to render verbatim when `eligible` is false.
Creates the invitation plus its pending guest seat (a GUEST-PAY-01 `pending_payment` booking that holds NO capacity until paid). `payer:'guest'` returns the link to share; `payer:'host'` additionally returns a Stripe Checkout URL (saved card, new card, or MobilePay). `return_base_url` must be an allowlisted host or it is ignored.
Withdraws an UNPAID invitation and releases its pending seat. A paid guest spot is a real booking — cancel it through the normal booking cancellation path so the venue's refund and fee rules apply (409 `ALREADY_PAID`).
GET/api/v1/guest-invites/{token}Public
Resolve a guest invitation (public)
GUEST-INVITE-01 — the invitation landing page a friend opens. Anonymous-allowed by design (the token is the capability); returns who invited them, the class, the price and the struck-through standard price, and nothing else about the host's account. `state` is `needs_account` for a signed-out visitor, `payable` once signed in, plus `already_paid` / `cancelled` / `expired` / `class_started` / `class_full`.
POST/api/v1/guest-invites/{token}/checkoutPublic
Pay a guest invitation without an account
GUEST-INVITE-01 — the Guest Visitor branch. ANONYMOUS-ALLOWED (the token is the capability): the invited friend pays without creating an account and receives a Stripe Checkout URL. Deliberately does NOT claim the seat, so no profile is created and `bookings.user_id` stays the host. Confirmation is still the verified-payment webhook. Trade-off the calling site MUST surface: with no login, only the host or the venue can cancel it afterwards. Refuses with 409 `ALREADY_CLAIMED` once someone has linked the invitation to an account.
GUEST-INVITE-01 — the class filled up before the invited friend accepted. ANONYMOUS-ALLOWED. An unpaid invitation never held a seat, so this is a normal outcome, not an error: the friend joins the waiting list and is NOT charged. If a spot opens, `reinviteWaitlistedGuests` sends a fresh payment link. Returns `{ position, already_on_waitlist }`.
The invited friend, now signed in, takes ownership of the guest seat and gets a Stripe Checkout URL. Claiming rebinds `bookings.user_id` to their profile (the host stays on `host_user_id`), which is what makes the spot appear in their own bookings and cancellable by them under the venue's ordinary cancellation rules. Capacity is still only taken by the verified-payment confirm RPC.
Returns the authenticated member’s appointments with the exact updated_at concurrency token required for cancellation. Supports upcoming/past direction, status, venue narrowing and cursor pagination.
Parameters, scopes and examples
Query parameters
directionstring
upcoming | pastDefault: upcoming
statusstring
Appointment status
organization_idstring
Optional venue UUID narrowing
POST/api/v1/appointmentsBearer token
Book my appointment
Creates a free, pass-covered, or pay-at-venue member appointment. Paid-at-booking appointments use the checkout endpoints below. When the venue payment mode is client_choice, omit payment_choice (or send online) to pay now — installed clients that do not send a choice stay on the paid checkout path. Send payment_choice=venue only for an unpaid create. X-Organization-ID and a stable Idempotency-Key are required; client communication follows the locked member-transactional policy rather than staff-selectable channels.
GET/api/v1/appointments/{id}Bearer token
Get my appointment
Returns one owned appointment, including its updated_at concurrency token. X-Organization-ID is required.
Parameters, scopes and examples
Path parameters
idstring · required
Appointment UUID
DELETE/api/v1/appointments/{id}Bearer token
Cancel my appointment
Atomically cancels one owned current appointment. Current clients send the exact rendered updated_at token; a stale token returns 409 STALE_TARGET and must be refreshed. Temporarily, an installed legacy member client may omit the body and the server binds its owned row snapshot to the same atomic CAS—there is no retry-without-CAS path. X-Organization-ID and a stable Idempotency-Key are required.
Read-only preview of the consequence of cancelling one owned appointment right now. The window and fee come from the service row (services.cancellation_window_hours / cancellation_fee_amount, defaults 24 / 0) — the exact pair the cancel RPC enforces — so the number shown matches the number charged. An appointment with a paid deposit or a linked payment is blocked with blocked_reason "refund_required" rather than previewing a self-service refund; a terminal appointment is blocked "not_cancellable". Honours the optional x-organization-slug tenant scope; an appointment outside the resolved scope, or belonging to another client, returns 404.
Parameters, scopes and examples
Path parameters
idstring · required
Appointment UUID
Response example
{
"data": {
"appointment_id": "uuid",
"can_cancel": true,
"blocked_reason": null,
"cancellation_window_hours": 24,
"is_late": true,
"will_charge": true,
"fee_amount": 250,
"currency": "DKK",
"refund_expected": false,
"message": "You are inside the venue’s cancellation window, so a late-cancellation fee applies."
}
}
GET/api/v1/appointments/quoteBearer token
Preview appointment payment policy
Returns the authenticated member’s server-authoritative concrete provider, effective service price, deposit, amount due at booking, remaining venue balance, payment timing, and payment_at_booking_mode (venue | online | client_choice). An `any` provider request resolves to one currently available provider before pricing. Optional payment_choice=online|venue is honoured only when the venue mode is client_choice; omitted choice defaults to online so older clients keep paying at booking. A configured deposit still requires the deposit online. Requires X-Organization-ID.
Parameters, scopes and examples
Query parameters
service_idstring · required
Service id
provider_idstring
Provider UUID or `any`; the response contains a concrete provider UUID
start_timestring · required
ISO appointment start
payment_choicestring
Optional online | venue. Omitted = pay now when the venue lets the client decide.
Claims a durable, tenant-bound operation before creating an account-pinned Stripe PaymentIntent. Returns PaymentSheet credentials. X-Organization-ID and a stable Idempotency-Key are required.
Parameters, scopes and examples
Exact live slot and provider selection. provider_id may be a concrete UUID or "any"; the server freezes one available provider and its effective price before payment.
Retrieves the exact account-scoped PaymentIntent, requires processor status succeeded, rechecks live policy, creates the appointment idempotently, and atomically links payment/accounting. Slot conflicts are compensated with an idempotent refund; 202 finalizing states are safe to retry. The Stripe webhook uses the same reconciler.
Atomically resolves all selected resources within the API-key venue, updates independent class/workshop allowance buckets, and reconciles active participants.
Add a paid-claim website applicant to a managed course roster
Trusted server-to-server bridge for venue application forms. Resolves the API-key tenant, the pass type's managed course, the applicant client/membership, and an optional localized track name; then creates or annotates an active roster enrollment and books its upcoming course sessions. Self-reported paid_deposit/paid_full values are retained as claims requiring reconciliation and never fabricate or overwrite BookingBible payment ledger state. API-key only (write:members), rate-limited, Idempotency-Key required.
Pay for a course enrollment (early-bird-aware price, or the deposit when required). Requires an Idempotency-Key header and returns a Stripe PaymentIntent client_secret + customer_id + ephemeral_key + stripe_account_id for the Payment Sheet. The enrollment is created `unpaid`; on `payment_intent.succeeded` it flips to paid/deposit_paid and its sessions are booked (deduped on the payment-intent id). Gated on membership + venue legal docs. Member-JWT. COURSE-SUITE — the body additionally accepts optional `plan` (payment-plan id), `purchaser_type` (`individual`|`company`), and `company` details (name/VAT/address) for VAT-by-purchaser + debtor invoicing. A supplied plan must exactly match a currently offered server-side plan; only an omitted property uses legacy/default behavior. The GET `/api/v1/courses/{id}` course detail additionally returns a `staff` array — `[{ role, name, title_label, photo_url, show_on_landing_page }]` — for the landing-page teaching team (COURSE-SUITE-02 multi-trainer). CV3-03 — the GET detail also returns `payment_plans` (`{ plans: [{ id, kind, installment_count? }], collection_method }`, the normalized plan OPTIONS this purchase route accepts as `plan`) and, for an authenticated Bearer caller with an enrollment, `viewer_enrollment` (`{ id, enrollment_status, payment_status, payment_plan, amount_paid, total_amount, balance, installments: [{ installment_number, amount, due_date, status }] }`; the response is always `Cache-Control: private, no-store`).
Revenue, bookings, attendance, 30-day active clients, and average revenue per client — per brand for the given period (default last 30 days). Uses bookings.brand_id and payments.brand_id populated by HYC_2. Returns venue-wide (unbranded) totals alongside the brand rows.
Parameters, scopes and examples
Required scopes
read:reports
GET/api/v1/admin/dashboard/todayBearer or API key
Today at a glance
Today's class timeline with booking counts, check-in status, and room assignments.
Parameters, scopes and examples
Required scopes
read:schedule
GET/api/v1/admin/scheduleBearer or API key
Admin schedule
Full schedule view with internal data: per-status booking counts, notes, cancellation reasons, updated_at concurrency tokens, and fail-closed historical capabilities. include_historical=true requires scheduling.manage_history.
Parameters, scopes and examples
Required scopes
read:schedule
Query parameters
start_datestring
Inclusive ISO date/time lower bound
end_datestring
Inclusive ISO date/time upper bound
include_historicalstring
Include protected historical class rows; requires scheduling.manage_historyDefault: false
GET/api/v1/admin/appointmentsBearer token
Venue appointment schedule
Business-app venue-wide appointment list with updated_at concurrency tokens and authoritative, fail-closed historical capabilities. Filters by ISO window, direction, status, provider, and location. Permission: bookings.manage.
Parameters, scopes and examples
Query parameters
fromstring
Inclusive ISO start time
tostring
Exclusive ISO end time
directionstring
upcoming | pastDefault: upcoming
statusstring
Appointment status
provider_idstring
Provider UUID
location_idstring
Location UUID
limitnumber
Maximum 200Default: 100
POST/api/v1/admin/appointmentsBearer token
Create an appointment for a client
Creates a tenant-bound current/future appointment for a known member or contact-complete guest through the canonical atomic appointment engine. Venue-local past dates and historical attestation fields fail closed until the dedicated executor is installed. Permission: bookings.manage. Idempotency-Key required. Client delivery is default-silent: only an explicit notification_channels selection of email, sms, and/or push can send. A non-empty selection requires notifications.send and an authoritative availability preflight before the mutation; an unavailable channel fails without creating the appointment. Omitted or empty channels and legacy notify booleans remain silent.
Tenant-bound client, provider, service, location, payment, notes, lifecycle state, updated_at concurrency token, and authoritative fail-closed historical capabilities for the Business app. Permission: bookings.manage.
Parameters, scopes and examples
Path parameters
idstring · required
Appointment UUID
PATCH/api/v1/admin/appointments/{id}Bearer token
Operate an appointment
Atomic ordinary check-in, start, complete, no-show, cancel, or reschedule with the exact expected_updated_at token returned by GET. A stale token returns 409 STALE_TARGET without mutation. Each action is checked against its canonical permission. Past/terminal appointments, past reschedule targets, and historical attestation fields fail closed until the dedicated executor is installed. Idempotency-Key required. No-show, cancel, and reschedule are default-silent and accept an explicit notification_channels selection of email, sms, and/or push; a non-empty selection requires notifications.send and an authoritative availability preflight before mutation. An unavailable channel fails without changing the appointment. Omitted or empty channels and legacy notify booleans remain silent. Check-in, start, and complete are non-client-contact actions and reject notification_channels.
Parameters, scopes and examples
Path parameters
idstring · required
Appointment UUID
Discriminated appointment action with the opaque updated_at token returned by the appointment detail
Dedicated, atomic appointment-history command contract. Requires a UUID Idempotency-Key, appointments.manage_history plus the ordinary operation permission, REWRITE attestation, and a past effective_at. Existing-row operations require the exact expected_updated_at and expected_status returned by the appointment detail; retrocreate accepts only the closed non-financial appointment intent. Corrections are always silent and reject notification controls. The route returns 503 HISTORICAL_EXECUTOR_UNAVAILABLE without table-call fallback until the separately reviewed correct_appointment_historical database RPC is installed.
Parameters, scopes and examples
Required scopes
appointments.manage_history
Path parameters
idstring · required
Appointment UUID
Closed appointment correction; history_confirmation_token must be REWRITE
Tenant-bound class and appointment reviews for the Business app. Supports source, visibility, rating, and pagination filters. Anonymous reviewer identity is never returned. Permission: feedback.view.
Returns the consent-, preference-, contact-, and suppression-aware email/SMS reach for a venue-scoped marketing audience. Permission: marketing.flash_sales.
Returns canonical refundable headroom, payer receipt contacts, SMS availability with an explicit disabled reason, original card brand/last4, venue refund destinations, and eligibility. Native clients must use this response instead of deriving refund options locally.
Creates a venue-scoped promo and public offer, optionally dispatching a consent-gated email/SMS campaign. Idempotency-Key required. Permission: marketing.flash_sales.
Explains current automatic review-request rules and returns anonymized recent delivery outcomes. Permission: marketing.reviews.
GET/api/v1/admin/gift-cardsBearer token
List venue gift cards
Returns the venue gift-card ledger for the Business app, including remaining balance. Optional code query looks up one card. Module gift_cards. Permission: gift_cards.sell.
Parameters, scopes and examples
Required scopes
gift_cards.sell
Query parameters
codestring
Exact gift-card code for a balance lookup
POST/api/v1/admin/gift-cardsBearer token
Issue a desk gift card
Creates a venue gift card. Delivery is default-silent: omitted send_notification never delivers. send_notification=true requires notifications.send plus a recipient. Idempotency-Key required. Permission: gift_cards.sell.
Delivery is default-silent. send_notification=true sends the inbox reply and requires notifications.send; otherwise the body is stored as an internal staff note. Idempotency-Key required. Permission: bookings.manage.
{
"body": "We can do Thursday at 10.",
"send_notification": false
}
GET/api/v1/admin/services/bundlesBearer token
List combo packages
Phone-useful combo-package list with price and item count. Full bundle builder stays on web admin. Permission: bookings.manage.
Parameters, scopes and examples
Required scopes
bookings.manage
GET/api/v1/admin/staffBearer token
List staff
Phone-useful staff directory: name, role, and contact. Payroll, commissions, and certifications stay on web admin. Permission: staff.view.
Parameters, scopes and examples
Required scopes
staff.view
GET/api/v1/admin/sites/statusBearer token
Website status snapshot
Returns the org-scoped venue website summary for the Business app: site identity, draft/published versions, preview URL, publish-readiness blockers/warnings, and connected custom-domain verification/SSL state. Permission: sites.view.
Parameters, scopes and examples
Required scopes
sites.view
POST/api/v1/admin/sites/publishBearer token
Publish the venue website
Publishes one tenant-bound website through the canonical publish core. Requires sites.publish plus a stable Idempotency-Key. Returns readiness blockers when the draft is not yet publishable; older mobile builds may ignore additive warnings.
Parameters, scopes and examples
Required scopes
sites.publish
Tenant-scoped website publish request
Request body
{
"site_id": "00000000-0000-4000-8000-000000000001",
"note": "Published after mobile review"
}
POST/api/v1/admin/sites/ai/chatBearer token
Talk to the website builder AI
Business-app SSE transport for the org-scoped website builder assistant. Requires sites.manage. Returns the mobile AI event vocabulary over Server-Sent Events and adds `site_patch` events so clients can refresh status mid-turn.
Parameters, scopes and examples
Required scopes
sites.manage
Tenant-bound site-builder message
Request body
{
"site_id": "00000000-0000-4000-8000-000000000001",
"conversation_id": null,
"message": "Make the homepage warmer and highlight workshops.",
"attachment_ids": [
"00000000-0000-4000-8000-000000000002"
]
}
POST/api/v1/admin/sites/attachmentsBearer token
Upload a website-builder attachment
Multipart upload for the Business website builder chat. Requires sites.manage. The file is stored through the shared AI attachment pipeline and later referenced by `attachment_ids` on the site chat route.
Parameters, scopes and examples
Required scopes
sites.manage
DELETE/api/v1/admin/sites/attachmentsBearer token
Delete a pending website-builder attachment
Deletes one tenant-owned, not-yet-bound website-builder attachment before it is sent in chat. Requires sites.manage.
Returns the frozen current allowance, published recurring choices, pricing version, and pending renewal change for an organization-owned issued pass. Permission: passes.manage.
Preview or schedule a client Flexible membership change
Previews or schedules a quantity/unlimited change for renewal 1–24 against an unexpired quote. Organization ownership is enforced and the new entitlement applies only after the target renewal invoice is paid. Permission: passes.manage.
Creates, replaces, or removes the one automatic clips-empty offer for a limited recurring pass or still-valid class pack. The target may be hidden from public catalogs. Permission: passes.manage.
Returns every active venue pass type for authenticated POS staff, including pass types intentionally hidden from public consumer catalogs. Permission: pos.access.
Business-app stream-control DTO for one tenant-bound class instance. Returns source options without provider live-stream ids, stream keys, RTMP URLs, SRT URLs, or playback URLs; action availability includes exact disabled reason codes. Visible to venue schedule/check-in readers and assigned staff roster readers. Source/go-live writes repeat module/settings gates; end remains available for safe live shutdown.
Sets or clears the occurrence-level stream source before the class goes live. Requires class.create, active streaming module/entitlement, a streamable class, an active RTMP/SRT source with an attached provider stream, tenant binding, lifecycle CAS status=scheduled, UUID Idempotency-Key, and no assigned-instructor owner-toggle block. The operation is silent; notification fields are rejected.
Starts a class stream from an existing class provider stream or an active selected RTMP/SRT source. Requires class.create, active streaming entitlement/module, streaming settings enabled, streamable class, venue-local class date, tenant binding, lifecycle CAS status=scheduled, provider-adapter enablement, UUID Idempotency-Key, and no assigned-instructor owner-toggle block. Provider enablement is compensated when the DB transition fails or loses a race unless the winner uses the same stream. The route does not mint new one-time provider streams or expose source credentials; it is operationally silent and rejects notification fields.
Ends a live class stream. Requires class.create, tenant binding, lifecycle CAS status=live, and UUID Idempotency-Key. The class completion commits before the shared-stream provider disable guard runs; the response reports provider_stop as disabled, skipped_shared, failed, or not_applicable. The route records streaming usage after the class completion commit. It is operationally silent and rejects notification fields.
Retries only the provider disable step after a class has already completed; it never re-completes the class or repeats lifecycle effects. Requires class.create, tenant binding, UUID Idempotency-Key, and an attached provider stream. The shared-stream guard is organization-scoped and no blocking occurrence identifier is returned. This route is operationally silent and rejects notification fields.
PHONE-PUBLISH-01: mints an ephemeral, class-scoped provider live stream plus a hashed single-use claim token, bound to one organization, one class occurrence, and the preparing user, with a short server-enforced TTL. Requires class.create OR the assigned instructor when the venue enables instructor go-live, active streaming module/entitlement, streamable class, venue-local class date, the phone_publisher_sessions kill switch, tenant binding, and a UUID Idempotency-Key. Returns non-secret session state and the one-time claim token; NEVER ingest URLs, stream keys, or provider resource ids. One active session per class; supersession refuses while another device is actively publishing with a fresh heartbeat.
PHONE-PUBLISH-01: atomically consumes the single-use claim token and returns short-lived RTMPS ingest material exactly once (Cache-Control: private, no-store). Only the session creator may claim. Deliberately NOT idempotency-cached — a duplicate claim returns CLAIM_ALREADY_USED and the recovery path is revoke + prepare a new session; the old secret is never re-displayed. No ingest material is ever stored server-side.
PHONE-PUBLISH-01: transitions the claimed session to publishing and returns the authoritative session/provider/class status snapshot. Never marks the class live — only the provider active webhook does. Owner-bound, tenant-bound, UUID Idempotency-Key required.
PHONE-PUBLISH-01: periodic liveness touch returning session state, provider connection status (ingest fields stripped), and class lifecycle status. When the provider confirms an active input and the class is still scheduled inside the phone-publisher window, the snapshot reconciles the class to live (CAS; the cron sweep is the backstop). Owner-bound and naturally idempotent, so no Idempotency-Key is required. A stale heartbeat makes a publishing session eligible for takeover by another authorized device.
PHONE-PUBLISH-01: ends the active phone publisher session, completes the class when this session took it live (usage recorded after the completion commit), and records a DURABLE provider-cleanup outcome — a failed teardown is surfaced in GET state and retried, never hidden. Requires class.create or the permitted assigned instructor, tenant binding, and a UUID Idempotency-Key.
PHONE-PUBLISH-01: revokes a session whose device was lost or reinstalled or should no longer publish. The provider stream is disabled and scheduled for deletion, killing any leaked ingest material; a fresh session must be prepared and claimed — the old secret is never re-displayed. Requires class.create or the permitted assigned instructor, tenant binding, and a UUID Idempotency-Key.
PHONE-PUBLISH-01: explicitly retries a failed or pending provider teardown for a terminal publisher session. Requires class.create or the permitted assigned instructor, tenant binding, and a UUID Idempotency-Key.
Parameters, scopes and examples
Required scopes
class.create
Path parameters
classInstanceIdstring · required
Class instance UUID
PATCH/api/v1/admin/schedule/{id}Bearer or API key
Edit class instance
Update start/end time, instructor, class type, capacity, or room on a single class instance. Capacity can never go below the current booked count (422 CAPACITY_BELOW_BOOKED — cancel bookings first; no force override). Instructor/room changes are conflict-checked; instructor must be qualified. Notifications default silent: omitted controls and legacy notify_attendees never send. An explicit notify audience plus channel sends the branded schedule-change email/SMS/push to participants and/or instructors on time/instructor/room changes and requires notifications.send. Idempotency-Key honored; audit_log carries per-field from/to diffs.
Atomic, immutable-ledger correction for an isolated past class. Requires a UUID Idempotency-Key, scheduling.manage_history plus scheduling.manage, expected_updated_at and expected_status for an existing row, a past effective_at, and typed REWRITE attestation. Cancellation-state corrections additionally require class.cancel. Classes with roster, financial, course, workshop, import, streaming, or other linked records fail closed for specialist review. Notifications are always silent.
Parameters, scopes and examples
Required scopes
scheduling.manage_history
Path parameters
idstring · required
Class instance ID
Bounded historical class-instance correction
Request body
{
"operation": "class_instance.correct_timing",
"expected_updated_at": "2026-08-20T09:00:00.000Z",
"expected_status": "completed",
"history_reason": "Signed instructor log confirms the recorded class time",
"history_confirmation_token": "REWRITE",
"effective_at": "2026-08-20T10:00:00.000Z",
"intent": {
"startTime": "2026-08-20T08:00:00.000Z",
"endTime": "2026-08-20T09:00:00.000Z"
}
}
Atomic, immutable-ledger correction for a past recurring availability window. Requires a UUID Idempotency-Key, availability.manage_history, staff_portal.availability, staff.edit for another staff member, expected_updated_at plus expected_is_active for existing rows, a past effective_at, and typed REWRITE attestation. The staff path and tenant-owned row pin ownership. Notifications are always silent.
Applies explicit venue-local start/end time, room, instructor, capacity, or class type to 1–50 tenant-owned classes with per-row conflict/failure results. Notifications default silent; an explicit notify audience plus channel requires notifications.send and sends only after each successful write. Idempotency-Key required.
Cancels 1–50 tenant-owned classes through the canonical cancellation engine with per-row results. Notifications default silent: omission and legacy notify_attendees never send. Explicit notify audience plus channel requires notifications.send; clients support Email/SMS/Push and instructors support Email/SMS. Push without a selected client audience returns 422. Idempotency-Key required.
POST/api/v1/admin/schedule/{id}/cancelBearer or API key
Cancel class
Cancel a class instance (bookings cancelled, clips restored, audit + webhook). Notifications default silent: omission and legacy notify_attendees never send. Explicit notify.{audience,channels} requires notifications.send; channel overrides (email/sms/push) AND with client preferences. Clients support Email/SMS/Push; instructors support Email/SMS, and Push without a selected client audience returns 422. Mixed client+instructor Email/Push notifies clients by Email/Push and instructors by Email. Idempotency-Key honored.
Parameters, scopes and examples
Required scopes
write:schedule
Path parameters
idstring · required
Class instance ID
POST/api/v1/admin/schedule/{id}/substituteBearer or API key
Assign substitute
Replace instructor for a class. Validates no scheduling conflicts across locations. Notifications default silent. Explicit notify audience plus channel requires notifications.send; client delivery is Push-only and substitute-instructor delivery is Email-only; selecting both audiences with Email+Push sends those exact legs. SMS or a channel without its supported audience returns 422. Idempotency-Key honored.
Parameters, scopes and examples
Required scopes
write:schedule
Path parameters
idstring · required
Class instance ID
GET/api/v1/admin/checkinBearer or API key
Venue-local check-in day strip
The venue-local day's classes for the native staff check-in screen (Business app): per-class check-in/waitlist counts, room/instructor, and the day-navigation gates (today vs. read-only past/future). Defaults `date` to the venue-local today when omitted; optional `location_id` (query param or X-Location-ID header) narrows to one location.
Parameters, scopes and examples
Required scopes
read:bookings
Query parameters
datestring
Venue-local date, YYYY-MM-DD. Defaults to the venue-local today.
location_idstring
Restrict results to one location. Also accepted as the X-Location-ID header.
GET/api/v1/admin/checkin/{classInstanceId}Bearer or API key
Attendee list
Class roster with member details, pass info, native course_access covering this booking, add-ons, included services, check-in status, and class/booking updated_at concurrency tokens. Whole-class cancellations retain the preserved roster and each attendee’s previous status; ordinary client cancellations remain excluded.
Parameters, scopes and examples
Required scopes
read:bookings
Path parameters
classInstanceIdstring · required
Class instance ID
Query parameters
include_historical_recordsstring
Include protected cancelled/late-cancelled roster rows; requires scheduling.manage_historyDefault: false
Send a bulk email or SMS to a selected subset of one class instance. Submitted booking_ids are intersected server-side with the organization's active or whole-class-preserved roster; ordinary cancellations and stale/foreign ids are dropped and counted as skipped. Caller-supplied contact data is never accepted. Uses the canonical consent/suppression-aware bulk senders and requires the can_view_client_contact_info membership toggle. Idempotency-Key is honored.
Parameters, scopes and examples
Required scopes
members.contact
Path parameters
classInstanceIdstring · required
Class instance UUID
Channel, roster booking ids, and message
Request body
{
"channel": "email",
"booking_ids": [
"00000000-0000-4000-8000-0000000000b1"
],
"subject": "Class update",
"message": "Hi {{first_name}} — here is an update about your class."
}
POST/api/v1/admin/checkin/{classInstanceId}/{bookingId}Bearer or API key
Check in member
Check a member into class. Validates late arrival cutoff.
Parameters, scopes and examples
Required scopes
write:checkin
Path parameters
classInstanceIdstring · required
Class instance ID
bookingIdstring · required
Booking ID
POST/api/v1/admin/checkin/{classInstanceId}/{bookingId}/noshowBearer or API key
Mark no-show
Mark member as no-show. Applies no-show fee if configured.
Parameters, scopes and examples
Required scopes
write:checkin
Path parameters
classInstanceIdstring · required
Class instance ID
bookingIdstring · required
Booking ID
GET/api/v1/admin/membersBearer or API key
List members
Membership-driven client list with exact pre-pagination status and pass filtering. Native course grants count as covering entitlement: those clients are status=active (not no_pass) and each row may carry additive active_course_access { course_name } | null. Search by name, email, phone or venue client ID; optionally filter by tag, active pass type, or canonical pass family. pass_type_id and pass_family combine with AND semantics. Every successful response, including zero-match pages, includes meta.pass_type_options and meta.pass_family_options. Options expose distinct active-client counts across the full authenticated venue before pagination; pass types include current active templates plus archived templates still held by active clients, including types hidden from public pricing and course/workshop-managed types.
Parameters, scopes and examples
Required scopes
read:members
Query parameters
searchstring
Search by name, email, or phone
statusstring
Client status: active, inactive, new, or no_pass
tagstring
Filter by member tag
pass_type_idstring
Filter by active pass type
pass_familystring
Filter by pass family: recurring, class_pack, time_based, or intro_offer
Register an active client or send a pending client invitation. Enforces the venue plan limit, requires members.edit and an Idempotency-Key, and records a PII-safe audit event.
Full member profile: passes with course-fulfillment provenance, canonical native course_access, recent bookings/payments, tags, scores, credits, referrals, client_display_id, plus server-authoritative total_bookings and last_visit_at, per-channel notification_availability (email/SMS/push with exact unavailable reasons), contact_details_visibility {email,phone} (independent member.view_email / member.view_phone AND the membership contact toggle; contact_details_visible remains email AND phone for older app builds), and payments_visible. A 409 PROFILE_MERGED is returned when this profile was merged away in this venue, with primary_user_id of the survivor. Contact disclosure is fail-closed on PII_AUDIT_FAILED.
Soft-deactivates only the active membership at the selected venue; it never deletes the shared profile or changes memberships at other venues. Delivery is silent by default. An explicit notify object may select Email, SMS, and/or Push, which requires notifications.send and an availability preflight before the deactivation commits. A post-commit delivery failure is returned separately as notification_failure and never restores access. Protected Admin/Finance memberships retain their shared lifecycle authorization checks. Idempotency-Key required. Permission: members.delete.
Parameters, scopes and examples
Required scopes
members.delete
Path parameters
idstring · required
Member user ID
Deactivation reason and optional explicit client delivery channels
Tenant-scoped pass history with an exact total and opaque keyset cursor. Returns up to 100 records per page and never exposes processor subscription identifiers.
Deterministically merges live bookings and imported historical visits. Historical rows carry record_source=migration_history and read_only=true. The total is exact across both stores. Every row also carries source, payment_status, class_instance.local_date and a permission-agnostic actions block (cancel, remove_waitlist, change_pass, correct_attendance, check_in, mark_no_show — all false on an imported row). meta.venue_today is the venue-local date the today gates were measured against; meta.stats (first page only, absent when after is sent) holds exact counts over the entire filtered set.
Parameters, scopes and examples
Required scopes
members.view_insights
Path parameters
idstring · required
Active member ID
Query parameters
limitinteger
Items per page (1–100)Default: 25
afterstring
Opaque next_cursor from the previous page
pass_idstring
Only bookings funded by this pass
cycle_startstring
Venue-local YYYY-MM-DD start of a pass usage cycle (inclusive)
cycle_endstring
Venue-local YYYY-MM-DD end of a pass usage cycle (exclusive)
scopestring
upcoming = class start after now and the seat still held, ordered soonest first, no imported history; past = the exact complement, newest first. Omitted returns the merged view.
fromstring
Venue-local YYYY-MM-DD, inclusive, on the class start (imported rows compare on visit_date)
tostring
Venue-local YYYY-MM-DD, inclusive; converted to the next local midnight so evening classes stay in range
statusstring
Exact match on live rows; imported rows match on their normalised status, so confirmed, waitlisted and pending_payment never match one
class_type_idstring
Only classes of this class type (excludes imported history)
instructor_idstring
Only classes whose PRIMARY instructor is this person — a substitute does not match (excludes imported history)
location_idstring
Only classes at this location (excludes imported history)
brand_idstring
Only classes whose class type belongs to this brand (excludes imported history)
Tenant-scoped payments with exact total, refunds, safe card display, invoice linkage, and explicit receipt capabilities. Processor IDs, client secrets, and raw receipt URLs are never returned.
Parameters, scopes and examples
Required scopes
members.view_insights
Path parameters
idstring · required
Active member ID
Query parameters
limitinteger
Items per page (1–100)Default: 25
afterstring
Opaque next_cursor from the previous page
statusstring
Only payments with this status. Anything outside the list is a 400. Omitted = every status.
fromstring
Venue-local YYYY-MM-DD, inclusive. Applied to the page AND the total, so meta.total is the filtered total.
tostring
Venue-local YYYY-MM-DD, inclusive. from after to, or a day no calendar has, is a 400.
Re-sends through the canonical POS sender to the member contact stored on the server. Only same-venue POS-backed receipt payments are eligible; arbitrary recipients and payment retry are not supported.
Parameters, scopes and examples
Required scopes
notifications.send
Path parameters
idstring · required
Active member ID
paymentIdstring · required
Same-venue payment ID
Receipt delivery channel advertised by the payment receipt capability
Request body
{
"method": "email"
}
GET/api/v1/admin/paymentsBearer token
Org-wide recent sales
Business-app contract C6: the venue's recent payments (all statuses), newest first, mirroring the web sales drawer rows — plain-language method label, card label, money bucket (captured | recorded | internal), and the drawer's refund-offer rule (`refundable` = settled non-guest rows with money remaining). Amounts are integer minor units (øre). Cursor-paginated (opaque keyset cursor), limit ≤ 50. Range resolves in the venue's timezone.
Business-app contract C7: executes a claimed review through canonical processRefund. Body { review_intent_id, confirmation?, amount?, reason?, client_receipt_comment?, destination_id?, method_reference?, notify_client?, receipt_channels?: ('email'|'sms'|'push')[] }. Destination and notes must match the immutable review. Email is selectable (suppression is audited). SMS works only when review context reports available; otherwise execution rejects before money moves with 422 REFUND_SMS_UNAVAILABLE and unavailable_reason. Success includes refund_id, printable bearer-authenticated PDF URL, and per-channel outcomes.
Parameters, scopes and examples
Required scopes
billing.refunds.same_daybilling.refunds.full
Path parameters
idstring · required
Payment UUID
Refund details (amount in MAJOR units)
Request body
{
"review_intent_id": "00000000-0000-4000-8000-000000000000",
"amount": 199,
"reason": "Client requested the refund",
"client_receipt_comment": "We hope to see you again soon.",
"destination_id": "original",
"notify_client": true,
"receipt_channels": [
"email",
"push"
]
}
GET/api/v1/admin/refunds/{id}/receiptBearer token
Download canonical refund receipt PDF
Bearer-authenticated, tenant-scoped, no-store PDF used by native print/share. Contains only the optional client receipt comment; the staff-only internal reason is never rendered.
Business-app contract C8: send a bulk email or SMS to participants of one class instance. Recipients are resolved server-side — the submitted booking_ids are intersected with the class's ACTIVE roster (confirmed/waitlisted/checked_in); stale ids are dropped and counted as skipped, and caller-supplied contact info is never accepted. Delegates to the same senders/consent semantics as the web check-in bulk bar (templates admin_bulk_email / admin_bulk_sms). Requires the TV-D can_view_client_contact_info membership toggle. Returns { sent, skipped }.
Parameters, scopes and examples
Required scopes
members.contact
Path parameters
classInstanceIdstring · required
Class instance UUID
Channel, roster booking ids, and the message
Request body
{
"channel": "email",
"booking_ids": [
"00000000-0000-4000-8000-0000000000b1"
],
"subject": "Tonight’s class moves to Room 2",
"message": "Hi {{first_name}} — we moved tonight’s class to Room 2. See you there!"
}
Business-app parity: the eligible one-class products for paid guest spots. Uses the same guest visitor permission and catalog core as the web check-in screen.
Business-app parity: add 1–20 guest spots as payment-link, paid-at-desk, or comp/free bookings. Payment-link delivery supports email, SMS, or both. Uses the same context-free core as web; Idempotency-Key required.
Parameters, scopes and examples
Required scopes
booking.checkin
Path parameters
classInstanceIdstring · required
Class instance UUID
Guest contact, count, payment mode, product, and delivery channels
Business-app contract C1: the venue's failed payments (payments.status='failed') in the trailing window (default 30 days), newest first, cap 100. Each row carries client + linked pass context, a plain-language method label, and `retryable` per the same pure decider the web Retry button uses. Amounts are integer minor units (øre).
Parameters, scopes and examples
Required scopes
members.view_insights
Query parameters
daysinteger
Trailing window in days (1–365)Default: 30
member_idstring
Client-account parity P3 — only this client’s failed payments (public display id or UUID). `count` is then that client’s count. An id that is not an active client of this venue answers 404.
Business-app contract C2: re-collect a failed payment through the canonical retry core (PaymentIntent confirm or off-session invoice pay on the SC2-resolved Connect account, driving handleInvoicePaid). The body may be empty for the provider default, or contain payment_method_id selected from the exact failed invoice/PaymentIntent customer wallet. A provider-proven legacy card_ default is accepted and retried as the customer default without an unsupported override. Idempotency-Key is required, atomically claimed before the provider charge, and bound to the venue, payment, and selected payment_method_id; simultaneous reuse cannot double-charge and reuse with another card returns 409. Returns status succeeded | requires_action | failed with a plain-language message.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
paymentIdstring · required
Failed payment UUID
Optional exact saved card override; omit the body to use the provider default.
Record an external settlement for a failed renewal
Business-app contract C3: the failed recurring-renewal invoice was paid through another channel (cash, bank transfer, MobilePay, external card terminal, other). Settles the Stripe invoice out-of-band so the canonical recovery reactivates the pass, attributing the recovered payments row to the real method. amount is integer minor units (øre). Idempotency-Key required.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
paymentIdstring · required
Failed payment UUID
Settlement details
Request body
{
"method": "bank_transfer",
"amount": 79900,
"paid_at": "2026-07-31",
"note": "Paid via bank transfer, ref 1234",
"notify_client": true
}
Business-app contract C4: comp the failed recurring-renewal cycle — the client keeps the period, 0 revenue is recorded (the recovered payments row is forced to 'comped' amount 0). Reason required. Idempotency-Key required.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
paymentIdstring · required
Failed payment UUID
Waive details
Request body
{
"reason": "Goodwill — studio closure week",
"notify_client": true
}
Business-app contract C5: venue-imposed suspension (distinct from the member freeze) — blocks bookings until unsuspended. Client delivery is silent by default and accepts only notify.audience.clients=true plus an explicit Email/SMS/Push selection; legacy notify_client/notify_channels inputs remain silent. A non-empty selection requires notifications.send, and an unavailable selected channel returns PASS_NOTIFICATION_CHANNEL_UNAVAILABLE before the pass changes. Idempotency-Key (UUID) required; replay returns the stored response and the stable delivery reference prevents re-sending. Delivery failure after the pass write is reported as notification.sent=false and never rolls the suspension back.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Optional reason and explicit, default-silent client notification choice
Business-app contract C5: lift a venue-imposed suspension. Body is optional and silent by default. Client delivery requires notify.audience.clients=true, an explicit Email/SMS/Push selection, and notifications.send; legacy booleans/arrays remain silent. An unavailable selected channel returns PASS_NOTIFICATION_CHANNEL_UNAVAILABLE before the pass changes. Idempotency-Key (UUID) required; replay never re-sends. A post-write delivery failure returns notification.sent=false without rolling the pass change back. 422 NOT_SUSPENDED for a pass that is not suspended.
Business-app contract C5 (PASS-REACTIVATE-01): flip an expired/cancelled NON-recurring pass back to active; a run-out window requires new_end_date ≥ venue-local today. Client delivery is silent by default and requires notify.audience.clients=true, explicit Email/SMS/Push channels, and notifications.send; legacy notification inputs remain silent. Unavailable channels reject before mutation. Idempotency-Key (UUID) required and replay never re-sends; delivery failure after the write returns notification.sent=false without rollback. Recurring memberships are refused (422 RECURRING_UNSUPPORTED) — restart via a real re-mint. Audit pass_reactivated + reverse_payload.
Current attendance state for one booking plus what a correction would do: current_status, any attendance fee and its refund payment, the venue no-show fee, whether a clip was consumed, and per-channel notification_availability (email/SMS/push with the exact unavailable reason) so the client-notify picker can disable what cannot be delivered. can_refund_fee is always false here — refunds live in Billing.
Flip a booking between checked in, no-show, booked and removed for today or a past day (a future class returns 422 FUTURE_ATTENDANCE, a cancelled class 422 CLASS_CANCELLED). Marking a no-show additionally requires bookings.mark_no_show and removing a visit requires booking.cancel_member. Client delivery is silent by default: only an explicit notify object with a non-empty channel set sends, which requires notifications.send and a per-channel availability preflight before the correction (422 ATTENDANCE_NOTIFICATION_CHANNEL_UNAVAILABLE, 502 ATTENDANCE_NOTIFICATION_PREFLIGHT_FAILED). The legacy notify_client boolean is accepted but never delivers. refund_fee returns 409 REFUND_REVIEW_REQUIRED — refund the charged fee from its protected payment detail first. Idempotency-Key required.
Parameters, scopes and examples
Required scopes
booking.checkin
Path parameters
classInstanceIdstring · required
Class instance ID
bookingIdstring · required
Booking ID
Target attendance state, fee/clip choices, and explicit client delivery channels
The client's passes that could fund this booking, each with why it is or is not eligible (clips remaining, end date, who shared it, whether it is the current pass, and whether using it would shift the pass start date). Eligibility comes from the booking engine itself, so an option can never be offered that the engine would reject.
Move a confirmed, attended, no-show or waitlisted booking to another of the client’s passes; the clip movement is atomic. When the new pass has not started yet and this class is earlier, the change returns 409 PASS_SHIFT_CONFLICT with the classes that would be orphaned so staff can cancel them and retry. 422 PASS_REASSIGN_ERROR for any other refusal. Idempotency-Key required.
The venue catalogue the Business app builds the Visits filter sheet from: active class types (with their brand), this venue’s instructors, active locations and active brands. Served under the same grant as the list these choices filter, so a staff member who can see every booking can always load the sheet. Every list is org-scoped.
Removes one saved card from an active client of this venue. Requires members.contact and an Idempotency-Key (≤128 chars, bound to the venue, the operator, this client and this card; reuse for another card returns 409). The card must be on the client’s provider-proven wallet — an unknown id and another client’s id answer the same 404 PAYMENT_METHOD_NOT_FOUND, never an existence oracle. A shared platform-wallet card owned by another home venue answers 403 WALLET_FORBIDDEN. If the card was the default, the profile column and the exact Stripe customer default are cleared first (rolled back if the processor refuses) and only then is the card detached; a processor refusal answers 502 STRIPE_DETACH_FAILED. Audited; the client is never contacted.
Makes the named saved card the client’s default for future off-session charges. Requires members.contact and an Idempotency-Key. Only a card can be a default — a non-card method answers 422 NOT_A_CARD. 404 PAYMENT_METHOD_NOT_FOUND for an id not on the proven wallet, 403 WALLET_FORBIDDEN for another home venue’s shared wallet, 502 STRIPE_UPDATE_FAILED when the processor refuses (the local column write is rolled back). Setting the card that is already the default is a 200 no-op with no audit row. The client is never contacted.
Clears the client’s default card so nothing is charged off-session without a fresh choice. Requires members.contact and an Idempotency-Key. The path names the card the operator believes is current: if it is NOT the client’s default any more the request is refused with 409 NOT_DEFAULT (carrying the real default) rather than clearing a different card. 403 WALLET_FORBIDDEN and 502 STRIPE_UPDATE_FAILED as above. The client is never contacted.
Parameters, scopes and examples
Required scopes
members.contact
Path parameters
idstring · required
Active member ID
pmIdstring · required
The card the operator believes is the current default
Emails a payment receipt for each selected payment of this venue’s active client, including non-POS rows the single-payment receipt/send route skips. Requires members.contact plus notifications.send on this request, and an Idempotency-Key. Body is { payment_ids: uuid[] } (1…100). Each item reports sent, deduped, ineligible, or failed. The client is contacted by email only; omission of the key is 400, a foreign client is 404.
What the desk charge form needs before it can be shown: the venue currency, the display VAT rate (decimal) and its label, and the venue’s accounting categories. Requires pos.sell. An empty categories list means the venue has not set any up — the app disables the form and points at Settings rather than charging into a required column.
Charges an ad-hoc amount to the client’s saved card off-session (a phone payment, a fee) and records the sale. Requires pos.sell and an Idempotency-Key. amount_minor is an integer in minor units and is VAT-inclusive — exactly what the card is charged. The card’s proven Stripe customer/account is what the PaymentIntent is created on and confirmed on. 201 on success with the new payment id, the receipt reference and the card label. 422 CHARGE_NOT_CHARGEABLE with reason payments_disabled | not_on_wallet | below_minimum | connect_not_active means nothing was attempted and the same key may be reused. 402 CHARGE_REQUIRES_ACTION means the card needs the client’s own authentication — the app then offers the secure card link. 402 CHARGE_DECLINED and 500 CHARGE_FAILED keep the key. set_as_default applies the card as the default afterwards, best effort; default_updated is null when it was not requested. No receipt is sent — the app offers the receipt route afterwards with the returned payment_id.
Parameters, scopes and examples
Required scopes
pos.sell
Path parameters
idstring · required
Active member ID
The card, the VAT-inclusive amount in minor units, and the bookkeeping fields
The client’s open no-show and late-cancel debt, newest first. A declined charge stays on the list — pending and failed both mean money is still owed. Requires passes.manage, the same grant the web read uses; collecting or waiving needs billing.refunds.full. Amounts are integer minor units beside their currency, and each row carries the class name and start time when the linked booking still has them.
Collects an open no-show or late-cancel fee from the client’s card. Requires billing.refunds.full and an Idempotency-Key. The fee is loaded scoped to this venue (404 otherwise) and must still be open — a charged, waived or refunded fee answers 422 FEE_NOT_UNPAID with its status before anything is reserved. A declined card answers 422 FEE_CHARGE_DECLINED and the debt stays on the client. No card on file, or card payments not ready for this venue, answers 422 FEE_NOT_CHARGEABLE and nothing was attempted, so the same key may be reused. The client is never contacted.
Records that an open fee was collected outside the card processor — cash, bank transfer, MobilePay, an external card terminal, or other. Requires billing.refunds.full and an Idempotency-Key. There is no amount: a fee is always settled for its own amount. The venue-scoped load and the 422 FEE_NOT_UNPAID check run before anything is reserved. The underlying write is atomic and idempotent on a re-run. A refusal answers 422 FEE_SETTLE_REJECTED with the reason. The client is never contacted.
Parameters, scopes and examples
Required scopes
billing.refunds.full
Path parameters
feeIdstring · required
Cancellation fee ID
How the money was collected, and an optional internal note
Request body
{
"method": "bank_transfer",
"note": "Paid at the desk, ref 1234"
}
Forgives an open no-show or late-cancel fee: no money is collected and no revenue is recorded. Requires billing.refunds.full and an Idempotency-Key. The venue-scoped load and the 422 FEE_NOT_UNPAID check run before anything is reserved. reason is optional and is recorded in the audit trail only. The client is never contacted.
Parameters, scopes and examples
Required scopes
billing.refunds.full
Path parameters
feeIdstring · required
Cancellation fee ID
Optional internal reason recorded in the audit trail
The saved cards a retry of this exact failed payment may use, default first. The wallet is read live from the failed invoice or PaymentIntent’s own Stripe customer on the account that payment was made on, so the picker can never offer a card the retry would then fail to charge. Requires passes.manage. 400 INVALID_PAYMENT_ID for a malformed id, 404 NOT_FOUND for another venue’s payment, and 422 RETRY_OPTIONS_UNAVAILABLE when the payment is not failed, has no client wallet, or has no processor customer.
Mints a one-time, seven-day link the customer can use to pay back a refund that already completed and should not have. Authority is the same one the web action uses: a venue owner or finance staff member who also holds billing.refunds.full (403 FORBIDDEN otherwise). Idempotency-Key required. The refund is loaded scoped to this venue (404 otherwise) and must be succeeded; 422 REPAYMENT_LINK_UNAVAILABLE carries reason not_succeeded | already_repaid | activated_link_exists | attribution_review_required | payer_unresolved | no_email. Nothing is sent: the platform never contacts the customer here — the operator shares the returned link themselves, exactly as on the web.
The stat tiles and revenue breakdown for a client’s Billing tab: what they owe, how much of that is overdue, their account credit (which may be negative), their gift-card balance, their lifetime spend and their spend so far this venue-local month, plus their spend split by accounting category. Requires payments.view. Every amount is an integer in minor units. Lifetime and month-to-date are exact server-side sums, not a sample of recent rows; totals_truncated is true only for the rare client whose succeeded-payment history exceeds the 50,000-row walk, and the two sums are then the newest 50,000 payments rather than the exact figure. Saved cards are deliberately not included here — read them from the payment-methods route, which resolves the account the cards actually live on.
A client’s recurring product subscriptions (lockers, rentals), newest first, with the price in minor units, the billing interval, the next billing date and any scheduled cancellation date. actions.cancel is true only while the subscription is active, pending or past due — the same rule the web section applies. Requires members.view_insights. The app shows the section only when the list is non-empty.
Stops a recurring product subscription. mode period_end lets the client keep what they already paid for; mode now ends it immediately. Requires products.manage and an Idempotency-Key. The subscription is loaded bound to BOTH this venue and this client (404 otherwise) and must still be cancellable — anything else answers 422 SUBSCRIPTION_NOT_CANCELLABLE with its status before anything is reserved. A processor refusal answers 502 STRIPE_UPDATE_FAILED and the local row is unchanged. Cancelling cannot be undone. The client is never contacted.
Client-account parity P1 (A9): push a pass's validity end date out, running the same core the web pass card uses (audit pass_extended carries the previous end date, so the change stays manually reversible). Permission: passes.manage. Idempotency-Key required; a replay returns the stored response. Client delivery is silent by default and requires notify.audience.clients=true plus an explicit Email/SMS/Push selection AND notifications.send on the same request; an unavailable selected channel returns PASS_NOTIFICATION_CHANNEL_UNAVAILABLE before the pass changes. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'extend' } when the pass cannot be extended.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
New end date and the explicit, default-silent client notification choice
Client-account parity P1 (A8): add or remove clips on a clip card. A zero delta is refused with 422 INVALID_DELTA, and the resulting balance floors at 0 (the web rule — staff can zero a card, never owe it). Permission: passes.manage. Idempotency-Key required; a replay returns the stored response. Client delivery is silent by default and requires an explicit channel selection plus notifications.send. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'adjust_clips' } when the pass has no finite clip balance.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Signed clip delta, optional reason, and the client notification choice
Client-account parity P1 (A10): move the start and/or end of a pass's validity window — the "transfer the activation date" case for manually sold or mis-dated passes. Provide at least one of new_start_date, new_end_date or new_duration_days; an exact new_end_date always wins over a duration. 422 HARD_END_EXCEEDED when the new end is past the pass type's absolute end date, 422 INVALID_RANGE for every other rejected window, each with the plain-language message the app shows verbatim. The response reports already_expired when the resulting window ends before venue-local today (allowed — backdating a correction is legitimate). This never touches bookings. Permission: passes.manage. Idempotency-Key required. Client delivery is silent by default. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'adjust_dates' }.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
New window (dates and/or duration), optional reason, notification choice
Request body
{
"new_start_date": "2026-02-01",
"new_duration_days": 90,
"reason": "Sold in January, first class in February",
"notify": {
"audience": {
"clients": true
},
"channels": {
"email": true,
"sms": false,
"push": false
}
}
}
Client-account parity P1 (A11): flip auto_renew, syncing Stripe cancel_at_period_end on the subscription's OWN account (a connected-account subscription is always scoped to passes.stripe_account_id), then write the admin-attributed audit row pass.auto_renew_toggled_by_admin. Permission: passes.manage. Idempotency-Key required. Client delivery is silent by default and requires an explicit channel selection plus notifications.send. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'toggle_auto_renew' }.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Desired auto-renew state and the client notification choice
Client-account parity P1 (A21): the venue's non-recurring pass types in name order, minus this pass's current type and minus retired native-managed course/workshop passes — the exact list the web pass editor offers. Read-only: no Idempotency-Key, 30 requests / 60 s per operator. Permission: passes.manage. 404 for a pass that is not this venue's. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'convert_type' } for a recurring or already-ended pass.
Client-account parity P1 (A21): convert a NON-recurring pass (clip card / time pass) to another of the venue's non-recurring types. Credit-balance aware — the unused share of a price drop is issued as account credit and reported as credit_issued_major, so an upgrade never surprise-charges mid-pass. 422 TARGET_RECURRING when the source or the target is a recurring membership (convert those from the subscription page), 422 TARGET_NOT_FOUND for an unknown target or a retired course/workshop pass. Permission: passes.manage. Idempotency-Key required. Client delivery is silent by default. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'convert_type' }.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Target pass type and the client notification choice
Client-account parity P1 (A22): move a pass to another client of the SAME venue, with both-ends safety — the pass must belong to this venue and the recipient must hold an ACTIVE membership here (422 RECIPIENT_NOT_CLIENT); a recipient who already owns the pass is refused with 422 RECIPIENT_IS_OWNER, and a recurring membership with 422 RECURRING_UNSUPPORTED (manage those from the subscription actions). A reason is required and recorded on the audit row. Both ends are notified through the selected channels — the new owner AND the previous one — so BOTH clear the per-channel preflight before the write. Find recipient_id with GET /admin/members?search=. Permission: passes.manage. Idempotency-Key required. Client delivery is silent by default and requires notifications.send. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'transfer' } for anything but an active non-recurring pass.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Recipient profile id, required reason, and the client notification choice
Client-account parity P1 (A20): hide an ENDED pass from the client profile without rewriting its lifecycle — history is preserved, the profile is decluttered. Only a terminated, expired or cancelled pass may be archived (422 ARCHIVE_NOT_TERMINAL: end or terminate it first); a pass that is already archived is an idempotent success no-op. Permission: passes.manage. Idempotency-Key required. This route NEVER notifies the client (the web never does): a notify body is accepted and ignored, and notification_summary always reports no channels. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'archive' }.
Client-account parity P1 (A20): bring an archived pass back into the client profile. A pass that is not archived is an idempotent success no-op. Permission: passes.manage. Idempotency-Key required. This route NEVER notifies the client (the web never does): a notify body is accepted and ignored. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'unarchive' }.
Client-account parity P1 (A23): cancel a non-recurring pass immediately, with no refund — the pass editor's "Cancel now", server-side. Cancellation is TERMINAL by design: there is deliberately no undo, and the audit row carries everything needed to reconstruct state. Refunds are NOT part of this call — review the exact payment from the client's Billing tab. early_termination_fee reports the fee this cancellation determined applies (null once the binding period is over); this route never charges it. Permission: passes.manage. Idempotency-Key required. The branded cancellation notice is silent by default and requires an explicit channel selection plus notifications.send. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'cancel_now' }; 422 RECURRING_UNSUPPORTED if a recurring membership reaches the core (terminate those from the subscription actions).
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Optional reason and the explicit, default-silent client notification choice
Client-account parity P1 (A4): the authoritative Stripe-cycle preview behind the freeze panel. POST carries the two dates but the call is READ-ONLY — it changes nothing, so it takes NO Idempotency-Key and is rate-limited as a read (30 requests / 60 s per operator). preview is null when the pass has no Stripe subscription and there is nothing financial to review. The freeze itself recalculates inside the idempotent financial engine, so this review is advisory: invalidate it whenever either date changes, and echo preview.previewToken as expected_preview_token on POST /admin/members/{id}/membership/pause (422 PREVIEW_STALE when it no longer matches). Permission: passes.manage. 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'freeze' } when the pass cannot be frozen.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Proposed freeze window (pause_end must be after pause_start)
Client-account parity P1 (A5): whether the pass's Stripe subscription is ACTUALLY paused, and whether that can be proven against the processor. Drives the "acknowledge unproven pause" confirmation on POST /admin/members/{id}/membership/resume, which accepts resume_from and acknowledge_unproven_pause. Read-only: no Idempotency-Key, 30 requests / 60 s per operator. A pass with no Stripe subscription answers hasStripePause=false, proven=true. Permission: passes.manage. 404 for a pass that is not this venue's; 422 PASS_ACTION_NOT_ELIGIBLE { capability: 'resume' } when the pass is not in a resumable state.
Client-account parity P1 (A13): returns the pass's current renewal collection method. Capability gate: billing_mode (422 PASS_ACTION_NOT_ELIGIBLE otherwise). 404 NOT_FOUND for an unknown or other-venue pass.
Switch a membership between auto-charge and venue-collected
Client-account parity P1 (A13): choose how FUTURE renewals are collected. 'external' flips the Stripe subscription to collection_method='send_invoice' (Stripe stops auto-charging but keeps raising cycle invoices, and dunning skips the pass); 'auto_charge' restores automatic card collection. The Stripe update is scoped to the pass's own connected account. Capability: billing_mode. Idempotency-Key required. Client delivery is silent by default and accepts only notify.audience.clients=true plus an explicit Email/SMS/Push selection, preflighted on the membership_admin_changed event before the pass changes. Errors: 422 BILLING_MODE_UNCHANGED when the pass already uses that method, 422 NO_CLIENT, 422 STRIPE_NOT_CONFIGURED, 500 STRIPE_UPDATE_FAILED / UPDATE_FAILED. Audit: pass.billing_mode_changed.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Target billing mode and an explicit, default-silent client notification choice
Client-account parity P1 (A14, EXTERNAL-MINT-01): record that this cycle of an externally billed membership was collected at the venue. Settles the subscription's OLDEST OPEN Stripe invoice through the canonical rails — recordManualSettlement for the attribution intent, then paid_out_of_band — so the invoice.paid recovery writes the payments row, rolls the period, resets clips and sends the receipt. Scoped to the pass's own Stripe account. Capability: billing_mode, and the pass must be billing_mode='external' (422 NOT_EXTERNALLY_BILLED). Idempotency-Key required. Never notifies. Errors: 422 NO_OPEN_INVOICE, 422 NO_SUBSCRIPTION, 422 STRIPE_NOT_CONFIGURED, 502 SETTLEMENT_FAILED / OPERATION_FAILED. Audit: payment.settled_externally (irreversible).
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
How the venue collected the cycle
Request body
{
"method": "bank_transfer",
"note": "Paid at the desk, ref 4471"
}
Client-account parity P1 (A15): what is outstanding on a failed recurring renewal — amount, when it failed, how overdue it is, whether booking is suspended, and any pending late fee that can still be waived. Deliberately NOT capability-gated: a pass with nothing outstanding returns recovery=null so the card can hide itself, and 404 NOT_FOUND (unknown or other-venue pass) is the only refusal.
Client-account parity P1 (A15): settle the outstanding renewal off-session on the client's saved card, as the venue. Success rides the canonical invoice.paid finaliser, so the pass reactivates, the period rolls and the receipt sends. Capability: renewal_recovery. Idempotency-Key required; empty body. Never notifies directly. Errors: 402 RENEWAL_REQUIRES_ACTION when the card needs the CLIENT to confirm (the app then offers the payment link — the client secret is never on this wire), 422 RENEWAL_CHARGE_FAILED with the processor's plain-language reason.
Client-account parity P1 (A15): email and/or SMS the no-login pay link for the outstanding renewal (deduped once per pass, failure and day). The notification IS the mechanism, so notify is REQUIRED: notify.audience.clients=true plus at least one of channels.email / channels.sms, else 400 CHANNEL_REQUIRED. Push is ignored. notifications.send is re-checked on this request and each channel is preflighted on the renewal_payment_link event before anything is sent (422 PASS_NOTIFICATION_CHANNEL_UNAVAILABLE / 502 PASS_NOTIFICATION_PREFLIGHT_FAILED). Capability: renewal_recovery. Idempotency-Key required. 422 RENEWAL_PAYMENT_LINK_FAILED when the client has no reachable address or the send fails. Audit: membership.renewal_payment_link_sent.
Client-account parity P1 (A15): delete the late-fee invoice item dunning attached to this failure, while it is still pending. Once the fee lands on a finalized invoice there is nothing to delete and the venue resolves it through the normal refund path — that case answers 422 NO_PENDING_LATE_FEE, checked before the write. Capability: renewal_recovery. Idempotency-Key required; empty body. Never notifies. 422 WAIVE_LATE_FEE_FAILED when Stripe refuses. Audit: membership.late_fee_waived.
Client-account parity P1 (A19): delete a membership SETUP row that never became a membership — only when Stripe proves the subscription is dead and nothing links to it. Real history is never deleted. expected_updated_at is the optimistic-concurrency token (409 STALE when the row moved). The Idempotency-Key IS the cleanup operation key and MUST be a UUID (400 IDEMPOTENCY_KEY_INVALID); the atomic RPC records it, so a retry whose response was lost replays the committed removal instead of a false 404. Capability: cleanup_failed_setup. Never notifies. Errors: 404 NOT_FOUND, 422 HAS_HISTORY / HAS_LIFECYCLE_HISTORY / PROCESSOR_UNKNOWN / NOT_RECURRING / NOT_SAFE / NOT_REMOVABLE with the web's messages. Audit: membership.setup_artifact_removed, or membership.setup_artifact_removal_denied on a refusal.
Client-account parity P1 (A16): the whole Share-on-the-pass state for one pass — its shares (with the recipient's display name, monthly cap and classes used this month), its pending invites, and the sharer-slot budget from pass_types.max_sharers. Capability: share (a pass type that allows no sharers is 422 PASS_ACTION_NOT_ELIGIBLE). 404 NOT_FOUND for an unknown or other-venue pass.
Client-account parity P1 (A16): add ONE sharer on the owner's behalf. Provide exactly one of recipient_user_id (an existing client of this venue → an immediate pass_shares row) or email (a pending pass_share_invites row whose token the recipient redeems); both or neither is 400 VALIDATION_ERROR. An email that already belongs to a client of the venue shares immediately instead, and the response says which happened. The invite email IS the mechanism, so there is no notify body. Capability: share, and the pass must be active (422 PASS_NOT_ACTIVE). Idempotency-Key required. Errors: 422 NO_SLOTS, 422 ALREADY_SHARED, 422 RECIPIENT_NOT_CLIENT, 422 OWNER_RECIPIENT, 422 INVALID_EMAIL, 422 WRITE_FAILED. Audit: pass_share_created / pass_share_invited.
Parameters, scopes and examples
Required scopes
passes.manage
Path parameters
passIdstring · required
Pass UUID
Exactly one of recipient_user_id or email, plus an optional monthly cap
Client-account parity P1 (A16): revoke an active share — the row goes status=revoked and the recipient drops out of passes.shared_with, which the booking RPC reads, so the shared capacity disappears immediately. Pass-scoped: a share on another pass or in another venue is the same 404 NOT_FOUND. Capability: share. Idempotency-Key required (no body; the share id rides the request fingerprint so a key reused for a different share is refused rather than replayed). Never notifies. Audit: pass_share_revoked.
Client-account parity P1 (A16): cancel a share invite before its token is redeemed, freeing the sharer slot it was holding. Only a still-pending invite can be cancelled — one already accepted or revoked answers 422 INVITE_NOT_PENDING. Pass-scoped: an invite on another pass or in another venue is the same 404 NOT_FOUND. Capability: share. Idempotency-Key required (no body; the invite id rides the request fingerprint). Never notifies. Audit: pass_share_invite_cancelled.
Accepts a Bearer JWT with `loyalty_price.grant` OR `passes.manage`. Unlike every other capability-gated pass read, a pass whose type offers no loyalty price and whose client holds no by-hand grant answers `{ context: null }` — never a 422.
Accepts a Bearer JWT with `loyalty_price.grant` (no API-key credential — this is a desk/termination-flow action). Requires an Idempotency-Key (≤128 chars). Issues the comeback offer with `origin: save_accepted` AND applies it immediately (`accepted_via: staff_save`), mirroring the web `acceptSaveOfferAtTermination`; notify is fixed silent. The app then continues its own termination flow.
Parameters, scopes and examples
Required scopes
loyalty_price.grant
Path parameters
idstring · required
Client id
The pass being saved and the win-back template to apply.
POST/api/v1/admin/loyalty-price/revokeBearer or API key
Take away a client’s by-hand loyalty price
Accepts exactly one credential: Bearer JWT with `loyalty_price.grant`, or an API key bound to the venue with `write:passes`. Requires a UUID `Idempotency-Key`, mirroring `loyalty-price/grant`. Writes through the context-free `revokeLoyaltyPriceCore` (the ONE writer).
Parameters, scopes and examples
Required scopes
write:passes
user_id + a short reason. Optional pass_id also clears a loyalty override on that pass.
Request body
{
"user_id": "uuid",
"reason": "Client asked to go back to the standard rate"
}
Accepts exactly one credential: Bearer JWT with `passes.manage`, or an API key bound to the venue with `write:passes`. Requires an Idempotency-Key (≤128 chars). Default-silent client notification — a non-empty channel selection requires `notifications.send` re-checked on the same request (JWT only; an API-key caller can never notify) plus a per-channel preflight before the mutation — refused as 422 `PASS_NOTIFICATION_CHANNEL_UNAVAILABLE` / 502 `PASS_NOTIFICATION_PREFLIGHT_FAILED`, the same codes every P1 pass route answers.
Parameters, scopes and examples
Required scopes
write:passes
pass_id + the agreed amount + a locked_until date (venue-local, must be after today) + a reason.
DELETE/api/v1/admin/rates/lock/{passId}Bearer or API key
Remove a pass rate lock
Accepts exactly one credential: Bearer JWT with `passes.manage`, or an API key bound to the venue with `write:passes`. Requires an Idempotency-Key (≤128 chars). Default-silent client notification — a non-empty channel selection requires `notifications.send` re-checked on the same request (JWT only; an API-key caller can never notify) plus a per-channel preflight before the mutation — refused as 422 `PASS_NOTIFICATION_CHANNEL_UNAVAILABLE` / 502 `PASS_NOTIFICATION_PREFLIGHT_FAILED`, the same codes every P1 pass route answers.
GET/api/v1/admin/rates/context/{passId}Bearer or API key
Get a pass’s full rate context
Accepts a Bearer JWT with `rates.view`, falling back to `passes.manage` on a 403 (mirrors the web `getPassRateContext`), or an API key bound to the venue with `read:passes`.
Client-account parity P4 (A27): the membership’s real billing history from the processor — the current cycle, whether it is set to cancel, the last twelve invoices with their paid/failed state and hosted links, the next payment, and the card on file. Requires passes.manage (not billing.manage — this mirrors the web editor’s own gate). Capability gate: subscription_timeline, so a non-recurring pass, an unfinished setup and a pass with no live subscription all answer 422 PASS_ACTION_NOT_ELIGIBLE. Every processor call is scoped to the pass’s own connected account. 502 STRIPE_ERROR when the processor is unreachable or unconfigured; 502 INCOMPLETE_PERIOD when it returns a billing period the app cannot render — retry. Nothing is written and the client is never contacted.
Client-account parity P4 (A29): shifts the membership’s next charge to a chosen date, with no proration — the canonical processor mechanism, scoped to the pass’s own connected account. Requires billing.manage, an Idempotency-Key and capability subscription_timeline. new_date is YYYY-MM-DD and must be in the future and within one year; anything else answers 422 INVALID_BILLING_DATE with the exact reason. 422 NO_STRIPE_SUBSCRIPTION when the membership has no recurring billing to shift. 502 STRIPE_ERROR frees the key for a corrected retry — the processor refused before anything moved. 500 DB_ERROR KEEPS the key, because the anchor already moved and a retry must not shift it twice.
Parameters, scopes and examples
Required scopes
billing.manage
Path parameters
passIdstring · required
Pass UUID
The new date, plus an optional explicit client-notify selection
Client-account parity P4 (A30): changes what the membership costs from its next payment onward. A new processor price is created on the SAME product and interval and swapped onto the subscription item with no proration; per-period overrides still win for the periods they cover. Requires billing.manage, an Idempotency-Key and capability subscription_timeline. amount_minor is an integer in the venue’s minor units and must be greater than zero. 422 NO_BILLABLE_ITEM when the subscription has no item to reprice. A 502 STRIPE_ERROR frees the key when the new price was never created, and KEEPS it when the price exists but the item swap failed — a retry must not create a second price. 500 DB_ERROR keeps the key (the processor already changed).
Parameters, scopes and examples
Required scopes
billing.manage
Path parameters
passIdstring · required
Pass UUID
The new recurring amount in minor units, plus an optional notify selection
Read a membership’s payment overrides and upcoming periods
Client-account parity P4 (A28): the SERVER-computed preview the web override panel renders, so the app never re-derives billing math. `preview` is the next `periods` billing windows (default 6, 1…36, anything else is 400 VALIDATION_ERROR) with each window marked as the base price or as a covering override, matched exactly as the invoice interceptor matches them. `overrides` is every saved row, ascending by start date; rows whose id appears in no preview period are the panel’s "Other overrides". A row with applied_payment_id set has already charged a real invoice and is locked (no edit, no delete). billing_active is false for a parked or migrated membership with no live subscription — overrides still save, they just stay staged until billing resumes, and billing_inactive_reason is the exact banner text. Requires billing.manage and capability subscription_overrides.
Parameters, scopes and examples
Required scopes
billing.manage
Path parameters
passIdstring · required
Pass UUID
Query parameters
periodsinteger
How many upcoming billing periods to preview (1…36)Default: 6
Client-account parity P4 (A28): sets an agreed price for upcoming membership payments. mode "periods" expands the membership’s real billing interval forward from its next payment date and writes one row per period; mode "range" writes one row covering an explicit window. amount_minor 0 is a comped period — nothing is charged. Requires billing.manage, an Idempotency-Key and capability subscription_overrides. 422 NO_UPCOMING_BILLING when "periods" is used on a membership with no next payment date; 400 INVALID_RANGE when the range ends before it starts; 500 DB_ERROR frees the key because nothing was written. `created` is how many rows were saved.
Parameters, scopes and examples
Required scopes
billing.manage
Path parameters
passIdstring · required
Pass UUID
Either the next N periods or one explicit window, plus an optional notify selection
Client-account parity P4 (A28): changes one not-yet-charged override’s amount and window in place. Requires billing.manage, an Idempotency-Key (bound to this pass AND this override, so a key cannot be replayed against another row) and capability subscription_overrides. The override is resolved only with both the venue and THIS pass, so an unknown id, another venue’s row and another pass’s row all answer the same 404 NOT_FOUND. 422 OVERRIDE_APPLIED when the row already charged a payment — it can no longer be edited. 400 INVALID_RANGE when the window ends before it starts. The answer is the row as stored, not the request echoed back.
Parameters, scopes and examples
Required scopes
billing.manage
Path parameters
passIdstring · required
Pass UUID
overrideIdstring · required
Payment override UUID
The corrected amount and window, plus an optional notify selection
Client-account parity P4 (A28): removes a not-yet-charged override so that period returns to the normal price. Requires billing.manage, an Idempotency-Key (bound to this pass and this override) and capability subscription_overrides. Same 404 NOT_FOUND rule as the PATCH. 422 OVERRIDE_APPLIED when the row already charged a payment — it cannot be removed retroactively. The body may be empty; send one only to choose notification channels.
Parameters, scopes and examples
Required scopes
billing.manage
Path parameters
passIdstring · required
Pass UUID
overrideIdstring · required
Payment override UUID
No fields required; send a notify selection only if the client should hear
Client-account parity P4 (A28): the panel’s multi-select writes. action "set" gives every selected period the same agreed amount — periods that already have an override are updated, base periods get a new row, and if the update fails the rows just inserted are removed again so the batch leaves nothing behind (which is why 500 DB_ERROR frees the key). action "restore" deletes the selected overrides so those periods return to the normal price, and is offered only when every selected period IS an override. Requires billing.manage, an Idempotency-Key and capability subscription_overrides. At most six periods or ids per call. 400 INVALID_RANGE for duplicate or inverted periods; 409 OVERRIDE_CONFLICT when a selected row no longer exists or a new period already has one — refresh and try again; 422 OVERRIDE_APPLIED when a selected row already charged a payment.
Parameters, scopes and examples
Required scopes
billing.manage
Path parameters
passIdstring · required
Pass UUID
Either the selected periods and one amount, or the override ids to restore
List the membership types this membership can convert to
Client-account parity P4 (A31): the venue’s other live recurring membership types, name-ordered, with each one’s price in the venue’s minor units and its billing cadence. The membership’s own type is excluded, and so are archived types — the change engine refuses those anyway, so offering one would be a dead end. Requires billing.manage and capability change_plan (a non-recurring, terminal or unfinished membership answers 422 PASS_ACTION_NOT_ELIGIBLE).
Client-account parity P4 (A31): quotes the change through the canonical membership-change engine and returns its object verbatim — the same shape GET /api/v1/admin/memberships/change already serves, including the signed, short-lived `quote` the confirm call must hand back. READ-ONLY: it changes nothing, needs NO Idempotency-Key, and runs on the read rate limit. Requires billing.manage and capability change_plan. This editor never offers a custom price, so the quote’s override_amount_minor is always null. Engine refusals map exactly as the memberships/change route maps them (404 / 403 / 409 / 422 / 402 / 503).
Client-account parity P4 (A31): applies the change the preview quoted. Requires billing.manage, an Idempotency-Key and capability change_plan, and the exact signed quote from the preview — its override_amount_minor MUST be null. Both this route and POST /api/v1/admin/memberships/change converge on the same durable quote fingerprint, so a change started on one cannot double-apply on the other. `replayed` is true when a repeated confirmation converged on an already-applied change; the client is not told twice. 409 QUOTE_STALE and the 422s free the key so the app can re-preview; 402 CHARGE_FAILED, 503 PARTIAL_APPLY, 503 CHANGE_IN_PROGRESS and 503 DATABASE_ERROR KEEP it, because the engine may already have moved money or claimed the quote.
Parameters, scopes and examples
Required scopes
billing.manage
Path parameters
passIdstring · required
Pass UUID
The target type, the signed quote from the preview, and an optional notify selection
Business POS read for an active venue client. Returns sanitized card references only (brand, last4, expiry, default, expired, chargeable); imported display-only cards are explicitly non-chargeable. Requires pos.access (register) OR members.contact (client record, the web admin gate). Never returns customer IDs, processor metadata, full card data, or client secrets.
Business POS card-setup operation for an active venue client. Requires an in-person consent attestation and Idempotency-Key. Returns the SetupIntent client secret, exact Stripe account namespace, legal merchant country, and frozen regional revision for native Payment Sheet.
Update the member phone number after an explicit staff confirmation. Requires members.edit and writes an audit record.
Parameters, scopes and examples
Required scopes
write:members
Path parameters
idstring · required
Member user ID
Supported member profile fields
Request body
{
"phone": "+4512345678"
}
POST/api/v1/admin/members/{id}/creditsBearer or API key
Issue account credit
Grant account credit to an active member (positive manual adjustment) on the same atomic, organization-scoped ledger path as the web action. The response balance is the canonical venue-available balance; profiles.credit_balance is maintained only as an account-wide compatibility cache. Currency must equal the venue currency (422 CURRENCY_MISMATCH). Client delivery is silent by default and requires an explicit canonical Email/SMS/Push selection plus notifications.send; membership and unavailable selected channels are rejected before the balance changes, and legacy booleans remain silent. Idempotency-Key is required (1–255 characters): an identical retry returns the original transaction and balance, while reuse for a different semantic request returns 409 IDEMPOTENCY_KEY_REUSE_MISMATCH.
Venue-wide duplicate suggestions for the clients-list merge wizard. Matches are never merged automatically. Requires members.merge plus members.contact and the acting membership’s full client-contact visibility.
Ranked duplicate candidates for the current client in this venue, excluding dismissed pairs. Requires members.merge plus members.contact and the acting membership’s full client-contact visibility.
Dismiss one candidate pair for this venue. Body: { candidate_user_id }. Idempotency-Key required; concurrent reuse is serialized before mutation. Requires members.merge plus members.contact and the acting membership’s full client-contact visibility, matching candidate review.
Fail-closed, venue-scoped merge preview with explicit transfer, retained-identity, indirect invoice descendant, and unsupported-data blocker counts. Unknown or unreadable ownership is never reported as zero. Body: { primary_user_id, secondary_user_id }. The path id must be one of those two. Requires members.merge plus members.contact and full membership contact visibility. Merge is notification-silent.
Parameters, scopes and examples
Required scopes
members.mergemembers.contact
Path parameters
idstring · required
Primary or secondary member ID
POST/api/v1/admin/members/{id}/mergeBearer token
Merge duplicate client profiles
Merges the secondary profile into the primary survivor using the transactional admin_merge_venue_profiles guard. Unsupported data, concurrent setup operations, provider-wallet/subscription ownership changes, and material row conflicts roll back without deactivating the source; retained identity/audit data remains explicit. confirm_token must be the literal MERGE. Notification-silent. Idempotency-Key is bound to venue, path member, and canonical body, with an atomic pre-mutation claim that serializes concurrent reuse. Requires members.merge plus members.contact and full membership contact visibility.
Parameters, scopes and examples
Required scopes
members.mergemembers.contact
Path parameters
idstring · required
Primary or secondary member ID
Survivor, merged-away profile, field choices, and MERGE confirmation
Issues one frozen-term comeback promise per client/template/settlement day, including after that promise closes. Delivery is silent by default; an explicit notify audience and channel set plus notifications.send is required to contact the client. Every selected channel is preflighted before the offer is created and the exact selection only narrows delivery—an SMS-only request can never fall back to email. The response reports actual delivery, and crash-safe deterministic provider/channel dedup permits a silent existing offer to be notified without double-send. Idempotency-Key is bound to venue, path member, and canonical body and atomically claimed before mutation. Permission: loyalty_price.grant.
Mark an open offer as declined (they said no) or revoked (withdrawn). Idempotency-Key required and atomically claimed before mutation. Permission: loyalty_price.grant.
Client-record invoices for this member. Drafts are excluded. Each row includes status, totals, and a share_url for the public invoice page. Permission: invoices.view. Org-wide invoice management remains /admin/invoices.
Venue-scoped family/partner/guest relationships for this client. Permission: members.view_insights. Related email fields are returned only when the caller also holds members.contact and full membership contact visibility; otherwise they are null.
Parameters, scopes and examples
Required scopes
members.view_insights
Path parameters
idstring · required
Active member ID
POST/api/v1/admin/members/{id}/tagsBearer token
Add a client tag
Add a manual member tag. Idempotency-Key required and atomically claimed before mutation. Permission: members.edit.
Parameters, scopes and examples
Required scopes
members.edit
Path parameters
idstring · required
Active member ID
DELETE/api/v1/admin/members/{id}/tagsBearer token
Remove a client tag
Remove a member tag. The tag travels in the query string (?tag=). Permission: members.edit.
Parameters, scopes and examples
Required scopes
members.edit
Path parameters
idstring · required
Active member ID
Query parameters
tagstring · required
Tag to remove
GET/api/v1/admin/bookingsBearer or API key
All bookings
Venue-wide booking list with filtering by date, status, class, member, and location.
Parameters, scopes and examples
Required scopes
read:bookings
Query parameters
fromstring
Start date (YYYY-MM-DD)
tostring
End date (YYYY-MM-DD)
statusstring
Filter by booking status
class_instance_idstring
Filter by class instance
user_idstring
Filter by member
location_idstring
Filter by location
POST/api/v1/admin/bookingsBearer or API key
Book for member
Create a confirmed or waitlisted booking on behalf of a member. Supports capacity override for admins. Client delivery is silent by default and requires an explicit Email/SMS/Push selection plus notifications.send; unavailable selected channels are rejected before booking/pass/count effects. The engine automatic notice is suppressed for this admin route so omission cannot send.
POST/api/v1/admin/bookings/waitlistBearer or API key
Add member to waitlist
Manually place a member on a class's waitlist at the queue tail. Always creates a waitlisted booking (never auto-confirms). Client delivery is default-silent and requires both an explicit client audience/channel selection and notifications.send; unavailable selected channels are rejected before queue effects.
DELETE/api/v1/admin/bookings/waitlist/{bookingId}Bearer or API key
Remove member from waitlist
Remove a waitlisted booking. Waitlisted rows only (409 on a confirmed booking); never triggers auto-promotion. Idempotent. Client delivery is default-silent and requires both an explicit client audience/channel selection and notifications.send; unavailable selected channels are rejected before queue effects.
Parameters, scopes and examples
Required scopes
write:bookings
Path parameters
bookingIdstring · required
Waitlisted booking ID
Optional reason and explicit client notification channels. Omit notify to stay silent.
POST/api/v1/admin/bookings/{bookingId}/cancelBearer or API key
Cancel a confirmed booking
Cancel a member's confirmed booking on their behalf (admin cancel semantics — may charge late fees + restore clips per policy; NOT the fee-free lapsed-booking path). Decrements booked_count, writes audit + booking.cancelled webhook, and issues a 30s undo ticket. Client delivery is silent by default and requires an explicit Email/SMS/Push selection plus notifications.send; unavailable selected channels are rejected before any mutation; legacy notify_client remains silent. Idempotent via Idempotency-Key. Returns 409 ALREADY_CANCELLED on a cancelled booking and 409 ON_WAITLIST for a waitlisted row (use the waitlist remove endpoint).
Bearer-JWT only. Correct one past class booking through the protected, organization-scoped historical ledger. Requires scheduling.manage_history plus the ordinary operation permission (bookings.manage for retrocreate, booking.checkin for attendance, booking.checkin + bookings.mark_no_show for no-show, or booking.cancel_member for cancellation/invalidation). Idempotency-Key must be a UUID. expected_class_updated_at is a compare-and-set token mandatory for every operation; expected_updated_at is a compare-and-set token additionally required for existing-booking operations. The executor refuses linked financial, pass, guest, waitlist, external, and other unsafe effects rather than inventing compensating semantics. Historical correction delivery is explicitly default-silent: Email, SMS, and Push are not available for this operation; a canonical explicit selection first requires notifications.send and then returns 422 before any mutation. Until the protected database executor has been installed, the route returns 503 HISTORICAL_EXECUTOR_UNAVAILABLE and never substitutes table calls.
Parameters, scopes and examples
Required scopes
scheduling.manage_history
Path parameters
bookingIdstring · required
Class booking ID
One closed class-booking correction. Every operation requires expected_class_updated_at; existing-booking operations also require expected_updated_at. history_confirmation_token must be REWRITE.
Bearer-JWT, passes.manage-scoped server-authoritative preview for a recurring membership. Resolves the selected client and saved card in the active venue, validates the venue-local start-date policy, and returns canonical buyer-specific gross pricing, registration fee/waiver, due-today amount, access date, first charge, next renewal, card label, contract/terms summary, and — when an operator discount schedule is requested — the resolved discount_schedule block with the agreed amount, the number of discounted periods and the first full-price charge date. This endpoint never mutates or charges.
Parameters, scopes and examples
Required scopes
passes.manage
Recurring membership options with exactly one of saved_payment_method_id (card-collected) or external_tender_method (venue-collected renewals, billing_mode=external). Optional registration_fee_discount applies a per-sale percentage discount to the one-time registration fee; legacy waive_registration_fee remains accepted. Optional discount_schedule sets an operator-agreed price for the first period, a fixed number of periods, or for as long as the membership runs; discount_reason stores the staff rationale with review, Stripe metadata, audit, and rate records.
Bearer-JWT, passes.manage-scoped recurring membership creation through the canonical subscription checkout core. Idempotency-Key is required. Re-resolves pricing, dates, saved-card ownership, Stripe locality, VAT/age band, concessions, and legal delivery before mutation. Returns the exact preview plus pass/subscription ids, payment status, and contract-delivery result. Off-session declines and SCA requirements are explicit 402 responses.
Parameters, scopes and examples
Required scopes
passes.manage
The same server-authoritative options accepted by the preview endpoint
Bearer-JWT, passes.manage-scoped option list for an organization-owned pass. Each option is buyer-priced by the canonical membership-change quote engine; a failed target is reported separately and cannot hide valid sibling options. Query: pass_id.
Parameters, scopes and examples
Required scopes
passes.manage
GET/api/v1/admin/memberships/changeBearer token
Preview a client membership change
Bearer-JWT, passes.manage-scoped server quote. Query: pass_id, target_pass_type_id and optional override_price_major. Returns exact charge, credit, effective date, next renewal and a short-lived signed quote binding; it never mutates or charges.
Parameters, scopes and examples
Required scopes
passes.manage
POST/api/v1/admin/memberships/changeBearer token
Apply a client membership change
Bearer-JWT, passes.manage-scoped confirmation through the canonical membership-change core. Requires Idempotency-Key and the exact signed quote returned by preview; foreign-venue passes resolve as not found and client-supplied prices are not accepted.
Process an idempotent point-of-sale transaction. Supports cash, venue credit, and a server-validated saved card. Saved-card SCA returns a 202 challenge response and is completed with a separate idempotent finalize request. Fresh cards, MobilePay and Stripe Terminal use their dedicated flows.
Recent POS transactions filtered by location and date. Refund headroom subtracts both succeeded and in-flight operation claims; refunded_amount reports succeeded claims and pending_refund_amount reports the reserved in-flight amount.
Parameters, scopes and examples
Required scopes
read:payments
POST/api/v1/admin/pos/transactions/{id}/receiptBearer or API key
Resend POS receipt
Resend a tenant-scoped POS transaction receipt by email or SMS. Uses the client's stored contact unless an explicit recipient is supplied. Idempotency-Key is required and retries of the same delivery action must reuse it.
Parameters, scopes and examples
Required scopes
write:pos
Path parameters
idstring · required
POS transaction ID
Receipt delivery channel and optional recipient override
Request body
{
"method": "email"
}
GET/api/v1/admin/pos/summaryBearer or API key
Daily POS sales summary
Daily sales breakdown for the given date (default today): totals (gross/discounts/VAT/credits/net) plus per-payment-method and per-transaction-type buckets. Completed transactions only; same date-window semantics as /admin/pos/recent.
Parameters, scopes and examples
Required scopes
read:payments
Query parameters
datestring
YYYY-MM-DD (default today)
location_idstring
Filter by location
GET/api/v1/admin/dashboard/revenue-seriesBearer or API key
Daily revenue series (sparkline)
Zero-filled daily revenue series ending today — succeeded payments bucketed by UTC day, matching the dashboard revenue_today semantics. days clamps to 1–90 (mobile uses 7 and 30).
Parameters, scopes and examples
Required scopes
read:reports
Query parameters
daysinteger
Window length in days (1–90)Default: 7
POST/api/v1/admin/members/{id}/membership/pauseBearer or API key
Pause membership
Pause (freeze) a member’s pass for a date window. Validated against the pass type’s pause policy; recurring memberships receive exact per-cycle billing credits on their own Stripe account; audit_log pass_paused. Client delivery is silent by default and requires notify.audience.clients=true plus explicit Email/SMS/Push channels and notifications.send. Legacy notification booleans remain silent. Selected-channel availability is rechecked before mutation; exact unavailable/preflight-failed responses leave the membership unchanged. Idempotency-Key honored. {id} accepts UUID or display ID (e.g. HYC-0042).
POST/api/v1/admin/members/{id}/membership/resumeBearer or API key
Resume membership
Resume a paused pass (Stripe-first ordering with compensating re-pause). Audit_log pass_resumed. Client delivery is silent by default and uses only an explicit canonical Email/SMS/Push selection. Selected-channel availability is rechecked before mutation; exact unavailable/preflight-failed responses leave the membership unchanged. Idempotency-Key honored.
Parameters, scopes and examples
Required scopes
write:members
Path parameters
idstring · required
Member user ID or display ID
Resume immediately or from a venue-local date. An unproven Stripe pause stays blocked unless acknowledge_unproven_pause types CLEAR PAUSE plus a reason. Optional explicit client delivery.
POST/api/v1/admin/members/{id}/membership/terminateBearer or API key
Cancel / terminate membership
Cancel or terminate a recurring membership with explicit effective dates: mode period_end (cancel at current cycle end), chosen_cycle (kth upcoming cycle, cycle required), or immediate. Runs the kill-switch-gated termination engine (fail-closed Stripe). Response carries the engine-confirmed effective_at. Client delivery is silent by default and requires an explicit canonical channel choice plus notifications.send; legacy booleans remain silent. Selected-channel availability is rechecked before mutation; exact unavailable/preflight-failed responses leave the membership unchanged. Idempotency-Key honored.
GET/api/v1/admin/members/{id}/membership/termination-previewBearer or API key
Termination preview (cycle picker)
Next 6 cycle boundaries (effective_at, venue-local last usable day, precedes-binding flag), venue policy defaults, and billing horizon for the terminate endpoint’s cycle picker.
Parameters, scopes and examples
Required scopes
read:members
Path parameters
idstring · required
Member user ID or display ID
Query parameters
pass_idstring
Pass ID (uuid)
POST/api/v1/admin/terminal/connection-tokenBearer or API key
Stripe Terminal connection token
Mint a Stripe Terminal connection token plus the venue Terminal location id (`{secret, location_id}`) for card-present readers and Tap-to-Pay. Ephemeral-token fetch — no Idempotency-Key (the Terminal SDK always needs a fresh token).
Parameters, scopes and examples
Required scopes
write:pos
POST/api/v1/admin/terminal/payment-intentBearer or API key
Create Terminal payment intent
Create a card-present PaymentIntent (manual capture) on the venue connected account. Returns `{client_secret, payment_intent_id}`. Money mutation — send an Idempotency-Key; replays return the cached response and the key is forwarded to Stripe.
Parameters, scopes and examples
Required scopes
write:pos
Payment intent
Request body
{
"amount": 12000,
"currency": "DKK"
}
POST/api/v1/admin/terminal/captureBearer or API key
Capture Terminal payment
Capture a confirmed card-present PaymentIntent. Returns `{captured: true, payment_intent_id}`. Money mutation — send an Idempotency-Key; an already-captured intent returns success.
Parameters, scopes and examples
Required scopes
write:pos
Capture
Request body
{
"payment_intent_id": "pi_xxx"
}
POST/api/v1/admin/terminal/receiptBearer or API key
Send Terminal receipt
Email or SMS a receipt for a captured Tap-to-Pay sale, resolved from the Stripe payment intent id. TERM-IDEMP-01: requires a caller-scoped Idempotency-Key; a replayed key returns the cached terminal response instead of re-sending.
POST/api/v1/admin/notifications/broadcastBearer or API key
Send broadcast
Send one or more push, email, and SMS channels to all members, selected member ids, or a server-resolved tag/pass/class audience. Idempotency-Key is required. Each channel derives a stable per-recipient delivery reference; a partial retry skips terminal successes/suppressions and resumes failed legs. Returns sent/skipped/failed counts per channel.
Parameters, scopes and examples
Required scopes
write:notifications
Broadcast
Request body
{
"channels": [
"email",
"sms"
],
"title": "New class added",
"target": {
"type": "tag",
"tag": "vip"
},
"subject": "New class added",
"body": "Check out our new Hot Power class on Saturday!"
}
GET/api/v1/admin/notifications/recentBearer or API key
Recent notifications
Recent email, SMS, and push notifications sent by the venue.
Parameters, scopes and examples
Required scopes
read:notifications
POST/api/v1/admin/staff/inviteBearer token
Invite a staff member
PROMPT_02 (S1-03) — provisions the auth user + profile + membership (status=invited), mints a staff_invitations claim token, and emails the venue-branded /auth/claim-invite link. Permission: staff.manage. The token is consumed by the WEB claim page (set password → membership flips invited→active); there is no separate accept API endpoint because the claim sets a password. 409 EMAIL_EXISTS when a Booking Bible account already exists for the email (adding an existing user as staff is a role change — use the admin UI). location_ids is stored on the invitation for record-keeping; location assignment remains a post-onboarding admin action. Emits staff.invited. Idempotency-Key supported.
Every staff shift in the venue for a date range. Permission: staff_scheduling.view. Joins staff profile name. Optional ?status= filter.
Parameters, scopes and examples
Query parameters
fromstring
Start ISO datetimeDefault: -7 days
tostring
End ISO datetimeDefault: +14 days
statusstring
Filter by ShiftStatus
POST/api/v1/admin/staff-scheduleBearer token
Create a staff shift
Create a new shift. Permission: staff_scheduling.manage. Note: API path skips the engine compliance pre-checks; for full compliance use the admin panel or the createShift server action.
POST/api/v1/admin/walk-in-queue/{id}/callBearer or API key
Call queue entry
Mark a queue entry as called and notify the client by SMS if they opted in. Permission: bookings.manage.
Parameters, scopes and examples
Required scopes
write:bookings
Path parameters
idstring · required
Queue entry ID
DELETE/api/v1/admin/walk-in-queue/{id}Bearer or API key
Remove walk-in
Cancel/remove a walk-in queue entry. Permission: bookings.manage.
Parameters, scopes and examples
Required scopes
write:bookings
Path parameters
idstring · required
Queue entry ID
GET/api/v1/admin/checkin/{classInstanceId}/qr-tokenBearer or API key
Get check-in QR token
Returns the current rotating QR token for a class instance. A new token is generated if none exists or the existing one is expired. Force rotation with ?refresh=true. Token TTL: 5 minutes. Permission: booking.checkin.
Accepts exactly one credential: Bearer JWT with `rates.view`, or an API key bound to the venue with `read:passes`. Dual credentials are rejected before authentication.
Accepts exactly one credential: Bearer JWT with `rates.view`, or an API key bound to the venue with `read:passes`. Dual credentials are rejected before authentication.
GET/api/v1/admin/rates/distributionBearer or API key
Get revenue distribution by rate
Accepts exactly one credential: Bearer JWT with `rates.view`, or an API key bound to the venue with `read:passes`. Dual credentials are rejected before authentication.
Accepts exactly one credential: Bearer JWT with `rates.view`, or an API key bound to the venue with `read:passes`. Dual credentials are rejected before authentication.
Accepts exactly one credential: Bearer JWT with `rates.view`, or an API key bound to the venue with `read:passes`. Dual credentials are rejected before authentication. The client and pass type must both belong to that venue.
Accepts exactly one credential: Bearer JWT with `rates.override`, or an API key bound to the venue with `write:passes`. Dual credentials are rejected before authentication.
DELETE/api/v1/admin/rates/override/{passId}Bearer or API key
Clear a pass rate override
Accepts exactly one credential: Bearer JWT with `rates.override`, or an API key bound to the venue with `write:passes`. Dual credentials are rejected before authentication.
Parameters, scopes and examples
Required scopes
write:passes
Path parameters
passIdstring · required
Pass id
Response example
{
"data": {
"ok": true
},
"error": null
}
GET/api/v1/admin/private-eventsBearer or API key
List private-event bookings
Accepts exactly one credential: Bearer JWT with `private_events.view`, or an API key bound to the venue with `read:private_events`. Targets are non-enumerating and venue-scoped.
Accepts exactly one credential: Bearer JWT with `private_events.manage`, or an API key bound to the venue with `write:private_events`. The canonical venue-scoped mutation workflow is used.
Parameters, scopes and examples
Required scopes
write:private_events
Private-event booking. Every PS-B2 field below is OPTIONAL and additive: client_id / save_as_client (link or create the client the session is for), partner_id + billing_target/billing_address/billing_vat_number/po_number/department/cost_center (bill a company — the billing block prefills from the partner record), brand_id, location_id, staff_note (a message the client sees), pricing_override ({mode: per_person|total, amount} — total is VAT-inclusive), and start_mode (confirmed | inquiry | confirm_on_payment) with payment_due_at. The legacy `status` field keeps working.
GET/api/v1/admin/private-events/{id}Bearer or API key
Get a private-event booking
Accepts exactly one credential: Bearer JWT with `private_events.view`, or an API key bound to the venue with `read:private_events`. Targets are non-enumerating and venue-scoped.
PATCH/api/v1/admin/private-events/{id}Bearer or API key
Update a private-event booking
Accepts exactly one credential: Bearer JWT with `private_events.manage`, or an API key bound to the venue with `write:private_events`. The canonical venue-scoped mutation workflow is used.
Parameters, scopes and examples
Required scopes
write:private_events
Path parameters
idstring · required
Booking id
Fields to update. PS-B2 adds the same optional fields the create route takes (client_id, partner_id + billing block, brand_id, location_id, staff_note, pricing_override, payment_due_at) plus `reprice` (recompute the frozen subtotal/VAT/total/deposit) and `notify_client` ({enabled, channels}). The response carries the client-visible change summary.
POST/api/v1/admin/private-events/{id}/approveBearer or API key
Approve a private-event booking
Accepts exactly one credential: Bearer JWT with `private_events.manage`, or an API key bound to the venue with `write:private_events`. The canonical venue-scoped mutation workflow is used.
POST/api/v1/admin/private-events/{id}/cancelBearer or API key
Cancel a private-event booking
Accepts exactly one credential: Bearer JWT with `private_events.manage`, or an API key bound to the venue with `write:private_events`. The canonical venue-scoped mutation workflow is used.
POST/api/v1/admin/private-events/{id}/quoteBearer or API key
Send a private-event quote
Accepts exactly one credential: Bearer JWT with `private_events.manage`, or an API key bound to the venue with `write:private_events`. The canonical venue-scoped mutation workflow is used.
Accepts exactly one credential: Bearer JWT with `loyalty_price.manage`, or an API key bound to the venue with `read:passes`. Dual credentials are rejected before authentication.
GET/api/v1/admin/loyalty-price/members/{memberId}Bearer or API key
Get a client loyalty-price context for a grant-capable operator
Accepts exactly one credential: Bearer JWT with `loyalty_price.grant`, or an API key bound to the venue with `write:passes`. Returns only the named active member’s programme label and standing; venue-wide configuration and aggregate counts remain manage-only.
POST/api/v1/admin/loyalty-price/grantBearer or API key
Give a client the loyalty price
Accepts exactly one credential: Bearer JWT with `loyalty_price.grant`, or an API key bound to the venue with `write:passes`. Requires a UUID `Idempotency-Key`; a replay is bound to the same venue, caller and grant payload. Writes through the context-free grant core (never a cookie action), so the audit trail and status recompute are identical to the admin web surface.
Parameters, scopes and examples
Required scopes
write:passes
user_id + a short reason. Optional venue-local expiry, and an optional pass_id with an agreed price on the catalog (MAJOR) scale — pass_price_override requires pass_id.
Request body
{
"user_id": "uuid",
"reason": "Agreed with the owner at the desk",
"expires_on": "2027-01-31",
"pass_id": "uuid",
"pass_price_override": 249
}
List every event on the unified calendar (classes, appointments, private events, streams, blocked time, instructor unavailability, blackouts, room rentals, maintenance, staff shifts, open gym) in a date range. JWT (any staff role) returns the org feed; API key with read:calendar returns the same. Filters: room_id, staff_id, location_id, brand_id, sources (comma-separated), only_blocking.
List maintenance slots in a date range. API key with read:maintenance scope. Filters: start, end (ISO datetime), room_id, status, limit (1-200, default 50).
Create a maintenance slot. API key with write:maintenance. Body: maintenance_type (preventive | corrective | inspection | deep_clean | equipment | renovation), title, start_time, end_time, plus optional priority, room_id, equipment_id, blocks_room (default true), assigned_staff_id, vendor_name, vendor_contact, estimated_cost, notes. Idempotency-Key header honored. When blocks_room is true and a room is set, conflicts against classes / appointments / private events / streams / room rentals / other maintenance return 409 with the conflict list. Emits maintenance.scheduled.
Parameters, scopes and examples
Required scopes
write:maintenance
Maintenance creation payload
Request body
{
"maintenance_type": "deep_clean",
"title": "Quarterly studio deep clean",
"start_time": "2026-05-01T20:00:00Z",
"end_time": "2026-05-01T22:00:00Z",
"room_id": "uuid",
"priority": "normal",
"blocks_room": true
}
Staff33 documented operations
GET/api/v1/staff/scheduleBearer token
My teaching schedule
Instructor's classes. Optional scope=own|partner|all; every row includes origin venue metadata and origin.timezone so apps bucket collaboration classes in the owning venue's local day.
GET/api/v1/staff/earningsBearer token
My earnings
Compensation, tips, and commissions broken down by period and class. tips_settled_via_collaboration is additive visibility for gratuities paid on a practitioner statement and is deliberately excluded from tips_received and total.
GET/api/v1/staff/classes/{id}/rosterBearer token
Class roster
View attendee list for a class the instructor is assigned to. Returns class and booking updated_at CAS tokens, venue-local day_state, and historical_capabilities. include_historical_records=true additionally exposes terminal roster rows and requires scheduling.manage_history.
Business-app staff correction using the same atomic ledger as Admin. Requires an active venue, roster access, class assignment where applicable, scheduling.manage_history, and the ordinary operation permission. Every operation requires expected_class_updated_at; existing rows also require expected_updated_at. Delivery is default-silent.
Parameters, scopes and examples
Path parameters
idstring · required
Class instance ID
bookingIdstring · required
Class booking ID
Bounded historical roster correction with UUID Idempotency-Key
Provider-scoped atomic correction for an isolated past class. The active provider must be assigned to the existing class; retrocreate must assign that provider directly, and assignment corrections must retain them. Requires schedule.view_own, scheduling.manage_history, scheduling.manage, UUID Idempotency-Key, CAS evidence for existing rows, past effective_at, and typed REWRITE. Notifications are always silent.
Mark or unmark a participant present for a course session ({user_id, present}). Idempotent; writes the same attendance store the web roster uses. Supports Idempotency-Key.
Send an email or SMS to course participants (audiences: enrolled, waitlisted, all, by track, by payment status, hand-picked). Requires course-manage scope; rate-limited; supports Idempotency-Key (retries never double-send).
Parameters, scopes and examples
Path parameters
idstring · required
Course ID
Message
Request body
{
"channel": "email",
"subject": "Bring a mat tomorrow",
"message": "Hi everyone — please bring your own mat to tomorrow’s session.",
"audience": {
"kind": "enrolled"
}
}
GET/api/v1/staff/availabilityBearer token
List my unavailable dates
Calling staff member's current and future unavailable dates for the selected venue. Permission: staff_portal.availability.
POST/api/v1/staff/availabilityBearer token
Set availability
Add or update unavailable dates for the calling staff member. Permission: staff_portal.availability.
Strict partial update of a current/future window using the exact updated_at token returned by GET. Caller must own the window; another instructor requires staff.edit. A stale token returns 409 STALE_TARGET. Existing or target ranges touching venue-local history fail closed until the dedicated executor is installed.
Parameters, scopes and examples
Path parameters
idstring · required
Window ID
Concurrency token plus one or more changed window fields
Sets is_active=false on a current/future window using the exact updated_at token returned by GET, after tenant and owner-or-staff.edit authorization. A stale token returns 409 STALE_TARGET. Historical ranges fail closed until the dedicated executor is installed.
Atomic, immutable-ledger correction for a past recurring availability window owned by the active staff member. Requires availability.manage_history, staff_portal.availability, UUID Idempotency-Key, expected_updated_at plus expected_is_active for existing rows, a past effective_at, and typed REWRITE. Notifications are always silent.
Parameters, scopes and examples
Path parameters
idstring · required
Availability window ID
Self-owned historical availability correction
Request body
{
"operation": "availability_window.invalidate",
"expected_updated_at": "2026-08-20T09:00:00.000Z",
"expected_is_active": true,
"history_reason": "Approved rota confirms that this window did not apply",
"history_confirmation_token": "REWRITE",
"effective_at": "2026-08-10T10:00:00.000Z",
"intent": {}
}
GET/api/v1/staff/substitute-poolBearer token
Read substitute-pool opt-in
Returns { enabled, updated_at } for the calling user's active org.
PUT/api/v1/staff/substitute-poolBearer token
Toggle substitute-pool opt-in
Set whether the calling user is available to be auto-suggested as a substitute. Body: { enabled: boolean }. Emits substitute_pool.opt_in_changed.
Parameters, scopes and examples
Opt-in state
Request body
{
"enabled": true
}
GET/api/v1/staff/appointmentsBearer token
My appointments
Cursor-paginated list of the calling provider's appointments, including venue currency and the same client name and 80-character provider-note preview shown in the web staff list. Contact details are not exposed. Query params: cursor (opaque next_cursor; legacy ISO timestamps are temporarily accepted), limit (1..100, default 25), status (one of the appointment status strings), direction (upcoming|past, default upcoming).
Parameters, scopes and examples
Query parameters
cursorstring
Opaque next_cursor returned by the previous page
limitnumber
Page size (1..100)Default: 25
statusstring
Optional status filter
directionstring
upcoming | pastDefault: upcoming
GET/api/v1/staff/appointments/{id}Bearer token
My assigned appointment detail
Provider-scoped detail with updated_at CAS evidence, server-authoritative historical review flags, privacy-gated client contact fields, and exact per-channel notification availability. The route always binds provider_id to the caller.
PATCH/api/v1/staff/appointments/{id}Bearer token
Act on my assigned appointment
Provider-scoped check_in, start, complete, no_show, cancel, or reschedule. Requires the action-specific appointments.*_own permission, exact expected_updated_at, and Idempotency-Key. Client notifications are default-silent and require explicit notification_channels plus notifications.send and server preflight. Past/terminal mutations fail closed until the appointment historical executor is installed.
Parameters, scopes and examples
Exact provider lifecycle intent and caller-rendered concurrency snapshot
Provider-scoped form of the dedicated atomic appointment-history command. Requires staff_portal.appointments, appointments.manage_history, the ordinary operation permission, a UUID Idempotency-Key, REWRITE attestation, and exact expected_updated_at plus expected_status CAS for existing rows. The existing appointment and any retrocreate or assignment target must remain assigned to the active provider. Corrections are always silent. Returns 503 HISTORICAL_EXECUTOR_UNAVAILABLE without table-call fallback until correct_appointment_historical is installed.
Parameters, scopes and examples
Required scopes
appointments.manage_history
Path parameters
idstring · required
Appointment UUID
The same closed appointment correction body as the admin route
Calling staff member's own non-instructor shifts (reception, cleaning, manager, front desk) in a date range. Use ?from=&to= ISO datetimes; defaults to next 14 days.
Parameters, scopes and examples
Query parameters
fromstring
Start ISO datetimeDefault: now
tostring
End ISO datetimeDefault: +14 days
POST/api/v1/staff/shifts/clock-inBearer token
Clock in
Clock in to an own staff shift. Allowed from 15 min before scheduled start through 30 min after. Sets status to in_progress and stamps clock_in_at. Emits shift.clock_in.
Parameters, scopes and examples
Shift to clock in to
Request body
{
"shift_id": "uuid"
}
POST/api/v1/staff/shiftsAPI key
Create a staff shift
Create a non-instructor staff shift. API-key only (write:staff). Body: start_time, end_time, optional staff_id, shift_type (regular | overtime | on_call | training | meeting), break_minutes, role_required, location_id, hourly_rate, notes. Idempotency-Key header honored. Emits shift.created (and shift.assigned if a staff_id is set).
Soft-cancel a shift (sets status=cancelled, preserves audit/payroll references). JWT (admin/manager) or API key with write:staff. Use ?reason= to attach a cancellation reason to the audit row.
Parameters, scopes and examples
Required scopes
write:staff
Path parameters
idstring · required
Shift UUID
Query parameters
reasonstring
Cancellation reason (free text)
POST/api/v1/staff/clockBearer token
Clock in or out (unified)
Unified clock-in/out endpoint. JWT only — resolves the staff member from the session token. Body: { action: "in" | "out", shift_id }. On clock-out the response includes actual_hours and total_pay. Emits shift.clock_in or shift.clock_out.
Parameters, scopes and examples
Clock action
Request body
{
"action": "in",
"shift_id": "uuid"
}
POST/api/v1/staff/shifts/clock-outBearer token
Clock out
Clock out of an in-progress staff shift. Computes actual_hours, actual_break_minutes, and total_pay (when hourly_rate is set). Returns warnings for break/EU compliance issues. Emits shift.clock_out.
Parameters, scopes and examples
Shift to clock out of
Request body
{
"shift_id": "uuid"
}
GET/api/v1/staff/time-offBearer token
My time-off requests
Latest 100 own time-off requests across all statuses.
POST/api/v1/staff/time-offBearer token
Request time off
Submit a new time-off request. Always created with status=pending. Manager approval/decline happens via the admin panel. Emits time_off.requested.
Create or update a lead for the venue. Public rate-limited (10 req/min/IP) or API-key authenticated (write:leads). Upserts on (organization_id, email): provided fields populate blanks, existing non-null values are preserved. Fires the lead_captured analytics event and emits a lead.created webhook with the full record plus an attribution object (utm_*, fbclid, gclid, landing_page, referrer).
Parameters, scopes and examples
Required scopes
write:leads
Lead payload. Org resolves from API key > X-Organization-ID header > subdomain > organization_id.
List leads for the API key's organization. API key only (JWT not permitted). Requires the read:leads scope.
Parameters, scopes and examples
Required scopes
read:leads
Query parameters
searchstring
Search by email, first_name, or last_name
sourcestring
Filter by source (website_form, exit_intent, referral, etc.)
statusstring
Filter by status (new, contacted, converted, unsubscribed)
pageinteger
Page numberDefault: 1
limitinteger
Items per page (max 100)Default: 20
Events3 documented operations
POST/api/v1/eventsPublic
Track an analytics event
Record a server-side analytics event into user_events. Public rate-limited (60 req/min/IP) or API-key authenticated (write:events). For conversion event names (purchase, subscribe, refund, lead_captured) we additionally fire Meta CAPI + GA4 MP when the venue has pixel credentials configured.
Parameters, scopes and examples
Required scopes
write:events
Event payload. UTM + click-id + page URL get merged into event_properties.
Create an open-gym session for a client. Validates an active pass with allow_open_gym=true and the access schedule. JWT users self-check in; API keys must include user_id.
Parameters, scopes and examples
Required scopes
write:bookings
Optional location, source, pass override, and notes.
Returns the latest version of each active required waiver for the authenticated client in the validated X-Organization-ID venue, including the exact markdown body to display and the latest version the client signed. `body_md` is canonical; `body` is the native-app compatibility alias with the same value.
Removes the caller's own club membership. Idempotency-Key supported; leaving a club you are not in is a no-op success. Emits club.member_left (audit + webhook).
Parameters, scopes and examples
Path parameters
idstring · required
Club id
Response example
{
"data": {
"ok": true
},
"error": null
}
DELETE/api/v1/clubs/{id}/membershipBearer or API key
Leave a club (membership alias)
REST-shaped alias for POST /clubs/{id}/leave used by the mobile clubs contract — identical behavior (Idempotency-Key, no-op success when not a member, club.member_left emit).
Parameters, scopes and examples
Path parameters
idstring · required
Club id
Response example
{
"data": {
"ok": true
},
"error": null
}
GET/api/v1/clubs/suggestionsBearer or API key
List club suggestions (admin)
Admin queue of client-submitted club suggestions. Filter by status.
Returns a single chat channel summary (channel row + unread_count + last_message_at) for the authenticated org. Staff role (or chat.read scope) required.
Bulk Scan Session — map a single scanned barcode to a preset target
Hot-path endpoint for the rapid-mapping Bulk Scan Session. Each call maps one scanned barcode. Returns status=created (new mapping), duplicate_in_session (same barcode+target already exists), requires_confirmation (different target, resend with allow_overwrite=true to proceed), or overwritten.
Model Context Protocol server (HTTP transport, JSON-RPC 2.0, protocol 2025-03-26). Authenticate with `X-API-Key`. Methods: `initialize`, `ping`, `resources/list`, `resources/read`, `tools/list`, `tools/call`. Read-only in v1. See `/developers/mcp` for the full guide.
Returns every registered feature module with its resolved enabled/settings/source for the calling venue. Resolution honors the four-tier precedence (tenant override → group lock → venue → group default → plan → default). Mobile Business app uses this for parity with /admin/settings/features.
Parameters, scopes and examples
Response example
{
"data": [
{
"key": "leaderboards",
"label": "Leaderboards",
"description": "Member-facing leaderboards by class type, period, and metric.",
"category": "Community",
"enabled": true,
"source": "plan",
"locked_by_group": false,
"settings": {}
}
]
}
PATCH/api/v1/admin/features/[moduleKey]Bearer or API key
Update a venue-level feature toggle
Flip enabled/settings for a feature module at the venue tier. Idempotency-Key supported. Returns 400 with `Locked by group: <paths>` when the venue tries to flip a group-locked toggle or write to a group-locked dot-path in `settings`. Audit-logged + emits `feature_toggle.changed` webhook.
Parameters, scopes and examples
Required scopes
write:settings
Path parameters
moduleKeystring · required
Module key from feature_modules.key
Partial update — only the fields you want to change.
Browse partner venues available to the member across the BOOKING BIBLE network. Each entry exposes a public summary plus the exact relationship status, active partnership id, and venue-level bookable flag for the organization selected by X-Organization-ID. Member-JWT.
Book a class at a partner venue using a network-eligible pass. Resolves the legal gate against the HOST venue’s documents before booking. Requires a caller-stable `Idempotency-Key` header; exact retries return the original booking and visit. Member-JWT.
Creates a venue-to-venue Network partnership request through the context-free Network mutation core. Bearer JWT requires network.manage; API keys require write:network.
GET/api/v1/network/partnerships/{id}Bearer or API key
Read a venue Network partnership
Reads one venue-to-venue Network partnership after proving the caller belongs to either party. Bearer JWT requires network.view; API keys require read:network.
PATCH/api/v1/network/partnerships/{id}Bearer or API key
Update a venue Network partnership
Updates a venue-to-venue Network partnership through the commercial lifecycle. Bearer JWT requires network.manage. API keys with write:network may change lifecycle status, but agreement negotiation and terms revisions require a verified human JWT administrator and return 403 for API-key callers.
DELETE/api/v1/network/partnerships/{id}Bearer or API key
Terminate a venue Network partnership
Terminates immediately only when binding and notice have both elapsed; otherwise schedules termination through the locked service RPC. Bearer JWT requires network.manage; API keys require write:network.
GET/api/v1/network/partnerships/{id}/visitsBearer or API key
List visits for a venue Network partnership
Lists visit ledger rows for one venue-to-venue Network partnership after proving the caller belongs to either party. Bearer JWT requires network.view; API keys require read:network.
GET/api/v1/network/partnerships/{id}/settlementsBearer or API key
List settlements for a venue Network partnership
Lists network-only settlements for one venue-to-venue Network partnership after proving the caller belongs to either party. Bearer JWT requires network.view; API keys require read:network.
Lists collaborations for the workspace selected by X-Organization-ID. Returns venue-owned relationships and invitations received by an owned professional workspace. Supplying the exact email query performs the privacy-preserving invitation lookup and requires network.manage.
Creates a pending venue-to-professional collaboration with an explicit venue role and compensation model. Requires network.manage and a non-individual venue workspace.
Accepts or declines an invitation from the professional workspace, or lets the venue set role, pause, resume, or terminate the relationship. Every operation is tenant-checked and requires network.manage.
Ends the relationship through the canonical termination core and suspends its venue membership unless keep_membership is explicitly true. Either party may terminate; requires network.manage.
Parameters, scopes and examples
Optional termination reason and membership handling
List the caller’s relationships (bidirectional — both relationships the member created and ones pointing back at them), hydrated with the linked member’s profile. Unlocks family pricing, shared booking, and pass sharing. Member-JWT, org-scoped.
Add a relationship. When `related_email` matches a member in the same venue, the relationship links to their profile and a mirror row is written so both members see it. Member-JWT. Idempotency-Key supported.
Submit a private-event inquiry as the authenticated member. Validates participant count against the event type’s min/max, computes pricing, and inserts a `private_event_bookings` row with `booked_by` set; emits `private_event.inquiry_created`. Member-JWT, org from X-Organization-ID. Idempotency-Key supported.
PROMPT_11 — returns the Stripe `client_secret`, frozen `merchant_country_code`, and `stripe_account_id` (`acct_*` for a direct Connect PI, otherwise null) for the booking’s deposit/full charge so the member can initialize Stripe Elements in the exact payment context. Customer/ephemeral-key credentials are returned only when this member owns the customer frozen by the first payment operation; admin-created or another accepted booker identity receives a safe generic sheet with null customer credentials. Idempotent (reuses the frozen PaymentIntent execution created at confirmation). `{ skipped: true }` when the event type’s payment_mode is `none`. Member-JWT; ownership by contact_email.
Member approves a quoted booking and gets Payment Sheet credentials
The member’s “Approve & pay” CTA: the booker confirms a quote the venue sent (status `quoted`) and receives the same frozen PaymentIntent, `merchant_country_code`, and `stripe_account_id` (`acct_*` only for direct Connect; otherwise null). Customer/ephemeral-key credentials are returned only to the user id that owns the frozen Stripe customer; another accepted booking identity receives a generic sheet with null customer credentials. Member-JWT; ownership by `booked_by` or `contact_email`. Idempotency-Key header REQUIRED — a retry re-enters the repairable confirmation pipeline and returns the same frozen execution (no duplicate PI, account drift, or regional drift). Honors the event type’s payment_mode via the shared helper; `{ skipped: true }` when payment_mode is `none` (invoice path).
PROMPT_11 — public catalog of a venue’s active, publicly-listed private-session types for the embeddable widget (/embed/private-sessions). Cross-origin access is governed by the platform dynamic CORS allowlist (venue custom domains). Cached, IP-throttled.
List products for the authenticated venue. Business-app JWTs require pos.access; API keys require read:products. Archived products are hidden unless include_archived=true.
Parameters, scopes and examples
Required scopes
read:products
Query parameters
include_archivedboolean
Include archived products. Defaults to false.Default: false
POST/api/v1/admin/productsAPI key
Create a venue product
Create through the canonical product mutation contract. Unknown/protected fields are rejected; initial stock creates one movement.
Parameters, scopes and examples
Required scopes
write:products
GET/api/v1/admin/products/{id}Bearer or API key
Get a venue product
Get one product only when it belongs to the authenticated venue. Business-app JWTs require pos.access; API keys require read:products.
Parameters, scopes and examples
Required scopes
read:products
Path parameters
idstring · required
Product UUID
PATCH/api/v1/admin/products/{id}Bearer or API key
Update a venue product
Update mutable catalog fields through the canonical product core. Business-app JWTs require products.manage; API keys require write:products. Products and its tier-gated Point of Sale dependency must be active. stock_quantity and protected fields are rejected.
Parameters, scopes and examples
Required scopes
write:products
Path parameters
idstring · required
Product UUID
DELETE/api/v1/admin/products/{id}API key
Archive a venue product
Archive through canonical catalog semantics (is_active=false plus archived_at). Permanent deletion is separate and guarded.
Parameters, scopes and examples
Required scopes
write:products
Path parameters
idstring · required
Product UUID
GET/api/v1/venues/{slug}/product-packagesPublic
List buyable clip cards
Public catalog of active product passes (clip cards) for a venue. Cached, IP-throttled.
Initiate a product-pass (clip card) purchase. A nonempty Idempotency-Key header is required and defines the durable operation. Returns Customer/ephemeral-key credentials in the same frozen Stripe account as the PaymentIntent, plus `merchant_country_code` and `stripe_account_id` (`acct_*` only for direct Connect; otherwise null). A service-only pre-provider claim binds tenant, catalog, customer, regional, routing, fee, amount, and currency; `product_passes` is granted atomically on `payment_intent.succeeded`. Gated on the `products` module + venue legal docs. Member-JWT.
Buy N paid tickets for a community event (`{id}` is the event booking id). Price = `member_price` + `guest_price` × (ticket_count − 1). Requires an Idempotency-Key header. Returns Customer/ephemeral-key credentials in the same frozen Stripe account as the PaymentIntent, plus `merchant_country_code` and `stripe_account_id` (`acct_*` only for direct Connect; otherwise null). On `payment_intent.succeeded` the ticket order flips to paid and a `going` RSVP is upserted with `guest_count = ticket_count − 1`. Gated on the `community_events` module. Member-JWT.
Strictly necessary cookies keep the site working. With your consent we use analytics and marketing cookies to improve the experience and measure ad performance. You can change this any time from your account’s privacy settings.