Skip to main content

REST API reference

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.
  • Date-based versions
  • Scoped API keys
  • Consistent JSON envelopes
400
Documented operations
30
Capability groups
63
Documented scopes
478
REST route handlers

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.

bash
curl https://bookingbible.com/api/v1/admin/dashboard \
  -H "X-API-Key: bb_live_…" \
  -H "X-Api-Version: 2026-04-11"

Endpoint registry

Scan the surface. Expand the exact contract.

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).

Parameters, scopes and examples

Query parameters

datestring · required
Required venue-local date (YYYY-MM-DD)

Response example

{
  "data": {
    "date": "2026-08-10",
    "classes": 3,
    "treatments": 1,
    "salon": 0
  },
  "error": null
}
GET/api/v1/venuesPublic

List venues

Search and discover venues. Supports text search, geo-location search with Haversine distance, city filtering, and pagination.

Parameters, scopes and examples

Query parameters

searchstring
Search by venue name, slug, or venue ID
latnumber
Latitude for location-based search
lngnumber
Longitude for location-based search
radiusnumber
Search radius in kmDefault: 10
citystring
Filter by city name
pageinteger
Page numberDefault: 1
limitinteger
Items per page (max 100)Default: 20

Response example

{
  "data": [
    {
      "id": "uuid",
      "name": "Acme Studio",
      "slug": "acme-studio",
      "address": "123 Example Street, Copenhagen",
      "class_count": 12,
      "location_count": 2
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 45,
    "has_more": true
  }
}
GET/api/v1/venues/{slug}Public

Get venue details

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.

Parameters, scopes and examples

Required scopes

read:schedule

Path parameters

slugstring · required
Venue URL slug

Query parameters

fromstring
Start date (YYYY-MM-DD)Default: today
tostring
End date (YYYY-MM-DD)Default: +7 days
location_idstring
Filter by location
brand_idstring
Filter by brand
class_type_idstring
Filter by class type
instructor_idstring
Filter by instructor
online_onlyboolean
Only online/hybrid classes

Response example

{
  "data": [
    {
      "id": "uuid",
      "start_time": "2026-09-05T08:00:00Z",
      "end_time": "2026-09-05T09:30:00Z",
      "status": "scheduled",
      "is_bookable": true,
      "requires_workshop_entry": true,
      "workshop_entry_target": {
        "kind": "workshop",
        "id": "uuid",
        "slug": "teacher-training-workshop"
      }
    }
  ],
  "error": null,
  "meta": {
    "page": 1,
    "limit": 50,
    "total": 1,
    "has_more": false
  }
}
GET/api/v1/classes/{id}Bearer token

Get a member-visible class instance

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.

Parameters, scopes and examples

Path parameters

idstring · required
Class instance UUID

Response example

{
  "data": {
    "id": "uuid",
    "status": "scheduled",
    "is_bookable": true,
    "requires_workshop_entry": true,
    "workshop_entry_target": {
      "kind": "course",
      "id": "uuid",
      "slug": "teacher-training-course"
    }
  },
  "error": null
}
GET/api/v1/venues/{slug}/pricingPublic

Get pricing & passes

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`.

Parameters, scopes and examples

Required scopes

read:pricing

Path parameters

slugstring · required
Venue URL slug

Query parameters

categorystring
Filter by pass category
location_idstring
Filter by location
brand_idstring
Filter by brand

Response example

{
  "data": {
    "org": {
      "slug": "demo-studio",
      "name": "Demo Studio",
      "currency": "DKK",
      "timezone": "Europe/Copenhagen",
      "vat_exempt_age_threshold": 30
    },
    "pass_types": [
      {
        "id": "uuid",
        "name": "Unlimited Monthly",
        "slug": "unlimited-monthly",
        "category": "membership",
        "price_amount": 899,
        "currency": "DKK",
        "is_recurring": true,
        "billing_interval": "month",
        "billing_interval_count": 1,
        "pricing_mode": "flexible_quantity",
        "active_pricing_version_id": "uuid",
        "flexible_pricing_config": {
          "schema_version": 1,
          "minimum_quantity": 1,
          "maximum_quantity": 20,
          "quantity_step": 1,
          "tiers": [
            {
              "up_to": 20,
              "price_per_class": 95
            }
          ],
          "unlimited": {
            "enabled": true,
            "price": 1100
          },
          "change_policy": {
            "member_changes_enabled": true,
            "allowed_cycle_offsets": [
              1,
              2
            ],
            "default_cycle_offset": 1
          }
        }
      }
    ]
  },
  "error": null
}
GET/api/v1/venues/{slug}/classesPublic

Get class 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).

Parameters, scopes and examples

Response example

{
  "data": {
    "country": "DK",
    "city": "Copenhagen"
  },
  "error": null
}
GET/api/v1/venues/{slug}/gift-cards/{code}/balancePublic

Public gift-card balance by code

PUBLIC (no auth) balance-by-code 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 code. 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

Response example

{
  "data": {
    "code": "GIFT-XXXX",
    "remaining_amount": 350,
    "currency": "DKK",
    "status": "active",
    "expires_at": null
  },
  "error": null
}
GET/api/v1/venues/{slug}/gift-cards/{code}/previewPublic

Public gift-card preview by code

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. Returns `{ code, gift_type, sender_name, gift_description, 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

Response example

{
  "data": {
    "code": "GIFT-XXXX",
    "gift_type": "pass",
    "sender_name": "Alex",
    "gift_description": "Unlimited Monthly (3 months)",
    "amount": 1500,
    "currency": "DKK",
    "status": "active",
    "expires_at": null
  },
  "error": null
}
POST/api/v1/venues/{slug}/inquiriesBearer token

Submit a booking inquiry

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.

Parameters, scopes and examples

Response example

{
  "data": [
    {
      "id": "uuid",
      "venue": {
        "slug": "hot-yoga-cph",
        "name": "Hot Yoga Copenhagen"
      },
      "service_name": "Deep Tissue Massage",
      "preferred_time": "Tuesdays or Thursdays after 17:00",
      "status": "Sent — waiting for the venue",
      "status_key": "sent",
      "created_at": "2026-08-10T12:00:00Z"
    }
  ],
  "error": null
}
GET/api/v1/venues/{slug}/addonsPublic

Booking add-on catalog

Products surfaced as booking add-ons (show_at_booking), filtered by the venue's per-category toggle and grouped by category. Public.

Parameters, scopes and examples

Path parameters

slugstring · required
Venue URL slug

Response example

{
  "data": {
    "addons": [
      {
        "id": "uuid",
        "name": "Bottled Water",
        "price": 25,
        "currency": "DKK",
        "category_id": "uuid"
      }
    ],
    "categories": [
      {
        "category_id": "uuid",
        "category_name": "Water & Drinks",
        "addons": []
      }
    ],
    "enabled_category_ids": []
  }
}
Authentication22 documented operations
POST/api/v1/auth/loginPublic

Login

Authenticate with email and password. Returns JWT access token and refresh token.

Parameters, scopes and examples

Credentials

Request body

{
  "email": "user@example.com",
  "password": "password123"
}

Response example

{
  "data": {
    "session": {
      "access_token": "eyJ...",
      "refresh_token": "xxx",
      "expires_at": 1234567890
    },
    "user": {
      "id": "uuid",
      "email": "user@example.com",
      "first_name": "John"
    }
  }
}
POST/api/v1/auth/signupPublic

Register

Create account and optionally join a venue. Supports referral codes and UTM tracking.

Parameters, scopes and examples

Registration

Request body

{
  "email": "user@example.com",
  "password": "password123",
  "first_name": "John",
  "last_name": "Doe",
  "organization_slug": "acme-studio"
}
POST/api/v1/auth/magic-linkPublic

Magic link

Send a passwordless login link. Mobile requests require an S256 device challenge and use a verified HTTPS callback.

Parameters, scopes and examples

Magic link request

Request body

{
  "email": "user@example.com",
  "code_challenge": "DwBzhbb51LfusnSGBa_hqYSgo7-j8BTQnip4TOnlzRo",
  "code_challenge_method": "S256"
}
POST/api/v1/auth/magic-link/verifyPublic

Verify magic link

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.

Parameters, scopes and examples

Bound mobile token hash or email OTP

Request body

{
  "token_hash": "token-hash-from-url",
  "type": "email",
  "request_id": "11111111-1111-4111-8111-111111111111",
  "code_verifier": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
}

Response example

{
  "data": {
    "session": {
      "access_token": "jwt",
      "refresh_token": "rt",
      "expires_at": 1234567890,
      "token_type": "bearer"
    },
    "user": {
      "id": "uuid",
      "email": "user@example.com",
      "active_organization_id": "uuid"
    }
  }
}
POST/api/v1/auth/signup/precheck-emailPublic

Signup email precheck

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.

Parameters, scopes and examples

Email to check

Request body

{
  "email": "user@example.com"
}

Response example

{
  "data": {
    "state": "absent"
  }
}
GET/api/v1/auth/mfa/factorsBearer token

List MFA factors

List the caller's enrolled TOTP MFA factors.

Parameters, scopes and examples

Response example

{
  "data": {
    "factors": [
      {
        "id": "uuid",
        "friendly_name": "Authenticator App",
        "factor_type": "totp",
        "status": "verified",
        "created_at": ""
      }
    ],
    "count": 1
  }
}
POST/api/v1/auth/mfa/enrollBearer token

Begin MFA enrollment

Start TOTP enrollment. Returns factor_id, QR code (SVG data URI), and raw secret. Factor is unverified until /mfa/verify confirms a valid code.

Parameters, scopes and examples

Friendly name for the authenticator device

Request body

{
  "friendly_name": "Authenticator App"
}

Response example

{
  "data": {
    "factor_id": "uuid",
    "qr_code": "data:image/svg+xml,...",
    "secret": "ABCDEF...",
    "uri": "otpauth://totp/..."
  }
}
POST/api/v1/auth/mfa/challengeBearer token

Mint MFA challenge

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.

Parameters, scopes and examples

Factor to challenge

Request body

{
  "factor_id": "uuid"
}

Response example

{
  "data": {
    "id": "uuid",
    "challenge_id": "uuid",
    "expires_at": 1764950400
  }
}
POST/api/v1/auth/mfa/verifyBearer token

Verify MFA code

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.

Parameters, scopes and examples

TOTP code or backup code

Request body

{
  "type": "totp",
  "factor_id": "uuid",
  "code": "123456"
}

Response example

{
  "data": {
    "verified": true
  }
}
DELETE/api/v1/auth/mfa/unenrollBearer token

Remove MFA factor

Unenroll an MFA factor for the caller.

Parameters, scopes and examples

Factor to remove

Request body

{
  "factor_id": "uuid"
}

Response example

{
  "data": {
    "removed": true,
    "factor_id": "uuid"
  }
}
POST/api/v1/auth/mfa/backup-codesBearer token

Generate MFA backup codes

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.

Parameters, scopes and examples

Required scopes

write:users

Target user + reason

Request body

{
  "user_id": "uuid",
  "reason": "Lost authenticator app"
}

Response example

{
  "data": {
    "user_id": "uuid",
    "factors_removed": 2,
    "backup_codes_invalidated": true
  }
}
POST/api/v1/auth/refreshPublic

Refresh token

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.

Parameters, scopes and examples

Recovery request

Request body

{
  "email": "user@example.com",
  "organization_slug": "yoga-bible"
}
POST/api/v1/auth/password/resetPublic

Reset password

Verify the single-use numeric code and permanently save a new password in one operation. Recovery tokens and reset links are not accepted.

Parameters, scopes and examples

Reset request

Request body

{
  "email": "user@example.com",
  "code": "482913",
  "new_password": "a-strong-new-password"
}

Response example

{
  "data": {
    "message": "Your password has been saved permanently.",
    "session": {
      "access_token": "jwt",
      "refresh_token": "refresh-token",
      "token_type": "bearer"
    }
  }
}
POST/api/v1/me/passwordBearer token

Change my password

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.

Parameters, scopes and examples

Code-verified password change

Request body

{
  "code": "482913",
  "new_password": "a-strong-new-password"
}

Response example

{
  "data": {
    "message": "Your password has been saved permanently.",
    "session": {
      "access_token": "jwt",
      "refresh_token": "refresh-token",
      "token_type": "bearer"
    }
  }
}
POST/api/v1/auth/handoff-codeBearer token

Mint SSO handoff code

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.

Parameters, scopes and examples

Destination domain (allowlist: namasteonline.com, namastestudios.dk, localhost)

Request body

{
  "target_domain": "namastestudios.dk"
}

Response example

{
  "data": {
    "code": "base64url-code",
    "expires_at": "2026-07-11T10:00:60Z",
    "target_domain": "namastestudios.dk"
  }
}
POST/api/v1/auth/handoff-exchangePublic

Exchange SSO handoff code

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.

Parameters, scopes and examples

The one-time code

Request body

{
  "code": "base64url-code"
}

Response example

{
  "data": {
    "session": {
      "access_token": "jwt",
      "refresh_token": "xxx",
      "expires_at": 1780000000,
      "token_type": "bearer"
    },
    "user": {
      "id": "uuid",
      "email": "user@example.com"
    },
    "target_domain": "namastestudios.dk"
  }
}
POST/api/v1/auth/device/codePublic

Mint TV device code pair

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.

Parameters, scopes and examples

Response example

{
  "data": {
    "device_code": "base64url-device-code",
    "user_code": "ABCD-EFGH",
    "verification_uri": "https://namasteonline.com/tv",
    "verification_uri_complete": "https://namasteonline.com/tv?code=ABCD-EFGH",
    "expires_in": 600,
    "interval": 5
  }
}
POST/api/v1/auth/device/approveBearer token

Approve TV device sign-in

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.

Parameters, scopes and examples

The TV's device_code

Request body

{
  "device_code": "base64url-device-code"
}

Response example

{
  "data": {
    "session": {
      "access_token": "jwt",
      "refresh_token": "xxx",
      "expires_at": 1780000000,
      "token_type": "bearer"
    },
    "user": {
      "id": "uuid",
      "email": "user@example.com"
    }
  }
}
Bookings10 documented operations
GET/api/v1/bookingsBearer or API key

My bookings

List authenticated user's bookings across all venues. Supports cursor and offset pagination.

Parameters, scopes and examples

Required scopes

read:bookings

Query parameters

statusstring
Filter: confirmed, waitlisted, cancelled, checked_in, no_show
upcomingboolean
Only future bookingsDefault: true
organization_idstring
Filter to one venue
pass_idstring
Only bookings funded by this pass
cycle_startstring
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.

Parameters, scopes and examples

Required scopes

write:bookings

Booking

Request body

{
  "class_instance_id": "uuid",
  "attendance_type": "physical",
  "pass_id": "uuid"
}

Response example

{
  "data": {
    "id": "uuid",
    "status": "confirmed",
    "class_instance": {
      "class_name": "Hot Yoga",
      "start_time": "2026-04-12T07:00:00Z"
    }
  }
}
GET/api/v1/bookings/{id}Bearer or API key

Get booking

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.

Parameters, scopes and examples

Registered-client class invitation

Request body

{
  "class_instance_id": "uuid",
  "recipient_email": "friend@example.com",
  "message": "Want to join me?"
}

Response example

{
  "data": {
    "invite_id": "uuid",
    "invitation_url": "https://venue.example/buddy-invite/token",
    "expires_at": "2026-08-01T17:00:00Z",
    "recipient_name": "Alex",
    "delivery_status": "sent"
  }
}
POST/api/v1/bookings/buddy/acceptBearer token

Accept a client class invitation

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.

Parameters, scopes and examples

Invitation token

Request body

{
  "token": "48-character-hex-token"
}

Response example

{
  "data": {
    "invite_id": "uuid",
    "booking_id": "uuid",
    "class_instance_id": "uuid",
    "status": "confirmed",
    "already_booked": false
  }
}
DELETE/api/v1/bookings/{id}Bearer or API key

Cancel booking

Cancel a booking. Returns cancellation fee if within late-cancel window. Clips are restored for clip-card passes.

Parameters, scopes and examples

Required scopes

write:bookings

Path parameters

idstring · required
Booking ID
POST/api/v1/bookings/{id}/cancelBearer or API key

Cancel booking (POST)

Alternate cancel endpoint for clients that cannot send DELETE requests. Same semantics as DELETE /bookings/{id}.

Parameters, scopes and examples

Required scopes

write:bookings

Path parameters

idstring · required
Booking ID
POST/api/v1/bookings/{id}/checkinBearer token

Self check-in

Member self check-in. Available within configured time window before class start.

Parameters, scopes and examples

Path parameters

idstring · required
Booking ID
POST/api/v1/class-instances/{id}/join-onlineBearer token

Join class online (live watch)

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.

Parameters, scopes and examples

Path parameters

idstring · required
Class instance ID

Response example

{
  "data": {
    "playback_url": "https://stream.mux.com/PLAYBACK_ID.m3u8?token=…",
    "playback_id": "PLAYBACK_ID",
    "expires_at": "2026-07-11T11:00:00Z",
    "booking_id": "uuid",
    "class": {
      "name": "Namasté Flow",
      "instructor": "Jane Doe",
      "start_time": "2026-07-11T10:00:00Z",
      "end_time": "2026-07-11T11:00:00Z"
    }
  }
}
POST/api/v1/qr/checkinBearer token

QR self-check-in

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.

Parameters, scopes and examples

QR token from scanned code

Request body

{
  "token": "a1b2c3d4e5f6...64hex chars"
}

Response example

{
  "data": {
    "booking_id": "uuid",
    "checked_in_at": "2026-06-01T10:02:13Z",
    "class_instance_id": "uuid"
  }
}
Passes19 documented operations
GET/api/v1/passesBearer or API key

My passes

List active passes across all venues with usage stats.

Parameters, scopes and examples

Required scopes

read:bookings
GET/api/v1/passes/{id}Bearer token

Pass detail

Single pass detail with usage stats, freeze/binding state, configurable recurring selection/allowance, and the pass-type + venue. Owner-scoped: cross-user reads return 404.

Parameters, scopes and examples

Path parameters

idstring · required
Pass id

Response example

{
  "data": {
    "id": "uuid",
    "status": "active",
    "clips_remaining": 8,
    "pass_type": {
      "id": "uuid",
      "name": "10-clip card"
    }
  },
  "error": null
}
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, and the published flexible recurring 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.

Response example

{
  "data": {
    "id": "uuid",
    "name": "10-clip card",
    "price_amount": 1299,
    "currency": "DKK",
    "category": "clip_card"
  },
  "error": null
}
POST/api/v1/passes/purchaseBearer token

Purchase pass

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. Configurable recurring passes require selection_kind=quantity with quantity, or selection_kind=unlimited. Fixed passes support promo codes, gift cards, and account credits.

Parameters, scopes and examples

Purchase

Request body

{
  "pass_type_id": "uuid",
  "organization_id": "uuid",
  "binding_months": 12,
  "selection_kind": "quantity",
  "quantity": 7
}

Response example

{
  "data": {
    "client_secret": "pi_xxx_secret_xxx",
    "customer_id": "cus_xxx",
    "ephemeral_key": "ek_test_xxx",
    "stripe_account_id": null,
    "merchant_country_code": "DK",
    "regional_revision": "org:venue-id:r4",
    "amount": 1299,
    "currency": "DKK",
    "breakdown": {
      "base": 1499,
      "binding_discount": -100,
      "promo": -50,
      "credits": -50,
      "total": 1299
    }
  }
}
POST/api/v1/gift-cards/redeemBearer token

Redeem a gift card

MEMBER (JWT) redeem endpoint so a branded storefront can host the whole redeem flow on its own domain. Applies the gift `{ code }` 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

Request body

{
  "code": "YB-GIFT-AB12"
}

Response example

{
  "data": {
    "type": "credit",
    "amount": 500,
    "newBalance": 500
  },
  "error": null
}
GET/api/v1/gift-cardsBearer token

My gift cards

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!"
}

Response example

{
  "data": {
    "gift_card_id": "uuid",
    "code": "YB-GIFT-AB12",
    "client_secret": "pi_xxx_secret_xxx",
    "amount": 500,
    "currency": "DKK",
    "gift_type": "custom_amount",
    "customer_id": "cus_xxx",
    "ephemeral_key": "ek_test_xxx"
  }
}
POST/api/v1/checkout/giftcard/payment-intentPublic

Start native gift card checkout

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"
}

Response example

{
  "data": {
    "client_secret": "pi_xxx_secret_xxx",
    "payment_intent_id": "pi_xxx",
    "amount": 50000,
    "currency": "DKK",
    "vat_amount": 0,
    "gift_type": "custom_amount"
  }
}
POST/api/v1/checkout/giftcard/confirmPublic

Confirm native gift card checkout

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.

Parameters, scopes and examples

Confirm gift card payment

Request body

{
  "payment_intent_id": "pi_xxx",
  "organization_slug": "hot-yoga-cph"
}

Response example

{
  "data": {
    "confirmed": true,
    "gift_card_id": "uuid",
    "gift_card_code": "AB12",
    "recipient_email_masked": "j***@example.com",
    "gift_type": "custom_amount"
  }
}
POST/api/v1/me/passes/{id}/pauseBearer token

Pause pass

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.

Parameters, scopes and examples

Path parameters

idstring · required
Pass id

Pause window

Request body

{
  "start_date": "2026-07-01",
  "end_date": "2026-07-21",
  "reason": "Holiday"
}
POST/api/v1/me/passes/{id}/resumeBearer token

Resume pass

Resume a paused pass and lift any Stripe billing pause. Owner-scoped. Idempotency-Key supported.

Parameters, scopes and examples

Path parameters

idstring · required
Pass id
POST/api/v1/me/passes/{id}/cancel-renewBearer token

Cancel pass auto-renew

Turn off a pass's auto-renewal, syncing Stripe cancel_at_period_end (best-effort). Owner-scoped. Idempotency-Key supported.

Parameters, scopes and examples

Path parameters

idstring · required
Pass id
POST/api/v1/me/passes/{id}/start-earlierBearer token

Start a deferred membership earlier

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)

Request body

{
  "new_start_date": "2026-07-03",
  "class_instance_id": "uuid (optional)"
}

Response example

{
  "data": {
    "pass": {
      "id": "uuid",
      "status": "active",
      "start_date": "2026-07-03"
    },
    "payment_status": "succeeded",
    "client_secret": null
  }
}
GET/api/v1/me/passes/{id}/termination-previewBearer token

Preview member membership termination

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.

Parameters, scopes and examples

Path parameters

idstring · required
Pass id

Response example

{
  "data": {
    "pass_id": "uuid",
    "pass_name": "10-Class Clip Card",
    "current_end_date": "2026-07-31",
    "proposed_end_date": "2026-08-14",
    "extension_price": 100,
    "currency": "DKK",
    "extension_period": 2,
    "extension_unit": "weeks",
    "extension_count": 0,
    "max_extensions": 2,
    "can_extend": true,
    "unavailable_reason": null,
    "payment_required": true
  }
}
POST/api/v1/me/passes/{id}/extension/payment-intentBearer token

Prepare pass extension payment

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.

Parameters, scopes and examples

Path parameters

idstring · required
Pass id

Response example

{
  "data": {
    "operation_id": "uuid",
    "status": "requires_payment",
    "payment_required": true,
    "amount": 100,
    "currency": "DKK",
    "proposed_end_date": "2026-08-14",
    "payment_intent_id": "pi_xxx",
    "client_secret": "pi_xxx_secret_xxx",
    "customer_id": "cus_xxx",
    "ephemeral_key": "ek_xxx",
    "stripe_account_id": "acct_xxx (direct mode; otherwise null)",
    "merchant_country_code": "DK",
    "regional_revision": "org:venue-id:r4"
  }
}
POST/api/v1/me/passes/{id}/extension/confirmBearer token

Confirm pass extension

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)"
}

Response example

{
  "data": {
    "extended": true,
    "status": "applied",
    "operation_id": "uuid",
    "new_end_date": "2026-08-14",
    "payment_required": true,
    "amount": 100,
    "currency": "DKK"
  }
}
POST/api/v1/passes/shares/acceptBearer token

Accept a pass share

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.

Parameters, scopes and examples

Accept share

Request body

{
  "token": "a1b2c3…"
}

Response example

{
  "data": {
    "pass_id": "uuid",
    "invite_id": "uuid",
    "shared": true
  },
  "error": null
}
Video2 documented operations
GET/api/v1/video-catalogBearer token

List video-on-demand catalog

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)

Response example

{
  "data": [
    {
      "id": "uuid",
      "title": "Vinyasa Flow — 12 June",
      "category": "class_recording",
      "duration_seconds": 3600,
      "playback_url": "https://stream.mux.com/abc.m3u8?token=...",
      "thumbnail_url": "https://image.mux.com/abc/thumbnail.jpg?token=...",
      "recorded_at": "2026-06-12T09:00:00Z"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 42,
    "has_more": true
  }
}
GET/api/v1/video-catalog/{id}Bearer token

Get VOD detail

Single video detail with signed playback URL. Pass_restricted videos require a qualifying active pass (returns 403 PASS_REQUIRED otherwise).

Parameters, scopes and examples

Path parameters

idstring · required
Video library entry UUID

Response example

{
  "data": {
    "id": "uuid",
    "title": "Vinyasa Flow — 12 June",
    "category": "class_recording",
    "duration_seconds": 3600,
    "playback_url": "https://stream.mux.com/abc.m3u8?token=...",
    "thumbnail_url": "https://image.mux.com/abc/thumbnail.jpg?token=...",
    "visibility": "members",
    "view_count": 17,
    "instructor": {
      "id": "uuid",
      "display_name": "Sarah",
      "avatar_url": null
    },
    "class_type": {
      "id": "uuid",
      "name": "Vinyasa Flow",
      "slug": "vinyasa-flow"
    }
  }
}
Forms3 documented operations
GET/api/v1/me/formsBearer token

List my intake forms

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.

Parameters, scopes and examples

Path parameters

idstring · required
Form UUID

Answer map keyed by field key

Request body

{
  "answers": {
    "full_name": "Jane Doe",
    "email": "jane@example.com",
    "injuries": "None"
  }
}

Response example

{
  "data": {
    "submission_id": "uuid",
    "form_id": "uuid",
    "status": "submitted",
    "thank_you": {}
  }
}
Profile67 documented operations
GET/api/v1/me/referralsBearer token

My referral status

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.

Parameters, scopes and examples

Response example

{
  "data": {
    "referral_code": "JANE-4821",
    "total": 3,
    "completed": 1,
    "pending": 2,
    "expired": 0,
    "rewards_earned": 100,
    "ongoing_discount_active": true,
    "reward": {
      "enabled": true,
      "reward_type": "points",
      "reward_value": 50,
      "referred_ongoing_discount_percent": 20
    },
    "referred": [
      {
        "name": "Sam P.",
        "status": "completed",
        "created_at": "2026-05-01T00:00:00Z",
        "completed_at": "2026-05-08T00:00:00Z"
      }
    ]
  }
}
GET/api/v1/me/venue-affinitiesBearer token

My venue affinities

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.

POST/api/v1/me/favorite-venues/{organizationId}Bearer token

Favourite a venue

Idempotently stars one active public venue for the authenticated member.

Parameters, scopes and examples

Path parameters

organizationIdstring · required
Venue organization UUID
DELETE/api/v1/me/favorite-venues/{organizationId}Bearer token

Unfavourite a venue

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.

Parameters, scopes and examples

Response example

{
  "data": {
    "contract_version": 1,
    "organization_id": "00000000-0000-0000-0000-000000000aaa",
    "booking_mode": "appointments",
    "business_type": "nail_salon",
    "operations": {
      "classes": {
        "enabled": false,
        "visibility": "hide"
      },
      "appointments": {
        "enabled": true,
        "management_enabled": true,
        "visibility": "default_on",
        "access_level": "full",
        "service_count": 8,
        "provider_count": 4,
        "plan_full_access": true
      }
    },
    "venue_surfaces": {
      "dashboard": true,
      "schedule": false,
      "check_in": false,
      "appointments": true,
      "clients": true,
      "point_of_sale": true
    },
    "active_modules": [
      "products"
    ],
    "vertical_modules": {
      "classes": "hide",
      "appointments": "default_on",
      "pos": "default_on"
    }
  }
}
GET/api/v1/meBearer token

My profile

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.

Parameters, scopes and examples

Workspace to make active

Request body

{
  "organization_id": "00000000-0000-0000-0000-000000000aaa"
}

Response example

{
  "data": {
    "organization_id": "00000000-0000-0000-0000-000000000aaa",
    "role": "admin",
    "permissions": [
      "network.view",
      "network.manage"
    ]
  }
}
GET/api/v1/me/featuresBearer token

My feature toggles

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.

Parameters, scopes and examples

Query parameters

organization_idstring · required
Venue to resolve entitlements for

Response example

{
  "data": {
    "organization_id": "uuid",
    "as_of": "2026-07-11",
    "can_book_physical": false,
    "can_watch_online": true,
    "online_only": true,
    "bookable_class_type_ids": [
      "uuid-a",
      "uuid-b"
    ],
    "active_passes": [
      {
        "id": "uuid",
        "pass_type_slug": "no-unlimited-online",
        "name": "Namasté Online Unlimited",
        "category": "membership",
        "grants": {
          "in_person": false,
          "online": true
        },
        "valid_from": "2026-07-01",
        "valid_to": null,
        "clips_remaining": null
      }
    ]
  }
}
PATCH/api/v1/meBearer token

Update profile

Update profile fields. Cannot modify role, balance, or org membership.

Parameters, scopes and examples

Profile fields

Request body

{
  "first_name": "Jane",
  "phone": "+4512345678",
  "marketing_consent": true
}
GET/api/v1/me/payment-methodsBearer token

List payment methods

Saved cards and payment methods from Stripe.

POST/api/v1/me/payment-methodsBearer token

Add payment method

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.

Parameters, scopes and examples

Response example

{
  "data": {
    "client_secret": "seti_xxx_secret_xxx",
    "setup_intent_id": "seti_xxx",
    "customer_id": "cus_xxx",
    "ephemeral_key": "ek_test_xxx",
    "stripe_account_id": null,
    "merchant_country_code": "DK",
    "regional_revision": "org:venue-id:r4"
  }
}
DELETE/api/v1/me/payment-methods/{id}Bearer token

Remove payment method

Detach a saved card from the Stripe customer.

Parameters, scopes and examples

Path parameters

idstring · required
Stripe payment method ID
PATCH/api/v1/me/payment-methods/{id}/defaultBearer token

Set default payment method

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.

Parameters, scopes and examples

Venue context

Request body

{
  "organization_id": "uuid"
}

Response example

{
  "data": {
    "customer_id": "cus_xxx",
    "ephemeral_key": "ek_test_xxx",
    "setup_intent_client_secret": "seti_xxx_secret_xxx",
    "apple_merchant_id": "merchant.com.bookingbible",
    "stripe_account_id": null,
    "merchant_country_code": "DK",
    "regional_revision": "org:venue-id:r4"
  }
}
POST/api/v1/me/push-tokenBearer token

Register push token

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.

Parameters, scopes and examples

Push token

Request body

{
  "token": "ExponentPushToken[xxx]",
  "platform": "ios",
  "device_id": "installation-uuid",
  "app_variant": "branded"
}
DELETE/api/v1/me/push-tokenBearer token

Deregister push token

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.

POST/api/v1/me/notifications/{id}/readBearer token

Mark one notification read

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.

GET/api/v1/me/notification-preferencesBearer token

Notification preferences

Returns the canonical ten-category catalog with effective email/SMS/push defaults, frequency caps, and per-member quiet hours for the active/requested organization.

PATCH/api/v1/me/notification-preferencesBearer token

Update notification preferences

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).

Parameters, scopes and examples

Path parameters

idstring · required
Invoice id
GET/api/v1/me/receipts/{paymentId}/pdfBearer token

Receipt PDF

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.

Parameters, scopes and examples

Path parameters

paymentIdstring · required
Payment id
POST/api/v1/me/receipts/{paymentId}/resendBearer token

Email me this receipt

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).

Parameters, scopes and examples

Feedback

Request body

{
  "class_instance_id": "uuid",
  "booking_id": "uuid",
  "rating": 5,
  "comment": "Amazing class!",
  "anonymous": false,
  "tip": {
    "amount": 29,
    "currency": "DKK",
    "anonymous": false
  }
}
GET/api/v1/post-attendance/eligibilityBearer token

Post-attendance actions

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.

Parameters, scopes and examples

Tip

Request body

{
  "source_type": "class",
  "source_id": "booking-uuid",
  "amount": 29,
  "currency": "DKK",
  "message": "Thank you!",
  "anonymous": false
}

Response example

{
  "data": {
    "tip_id": "uuid",
    "client_secret": "pi_xxx_secret_xxx",
    "payment_intent_id": "pi_xxx",
    "amount": 29,
    "amount_major": 29,
    "amount_minor": 2900,
    "amount_unit": "major",
    "currency": "DKK",
    "merchant_country_code": "DK",
    "source_type": "class",
    "source_id": "booking-uuid",
    "available_payment_methods": [
      "card",
      "mobilepay"
    ],
    "customer_id": "cus_xxx",
    "ephemeral_key": "ek_xxx",
    "stripe_account_id": null,
    "is_anonymous": false,
    "instructor_id": "uuid"
  },
  "error": null
}
POST/api/v1/tips/{id}/confirmBearer token

Confirm tip payment

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.

Parameters, scopes and examples

Path parameters

idstring · required
Tip id

Response example

{
  "data": {
    "id": "uuid",
    "status": "succeeded",
    "amount": 29,
    "currency": "DKK",
    "is_anonymous": false,
    "instructor_id": "uuid",
    "created_at": "2026-07-06T12:00:00.000Z"
  },
  "error": null
}
GET/api/v1/me/suppression-statusBearer token

Email/SMS suppression status

Self-service check of the user's email/SMS suppressions. COMMS-1 — LOCKED shape: `data.suppressions[]`, each `{ category: 'marketing'|'transactional'|'system', channel: 'email'|'sms', reason, suppressed_at }`. See docs/api/suppression-status.md.

Parameters, scopes and examples

Response example

{
  "data": {
    "suppressions": [
      {
        "category": "marketing",
        "channel": "email",
        "reason": "user_unsubscribed",
        "suppressed_at": "2026-06-09T12:00:00.000Z"
      }
    ]
  },
  "error": null
}
POST/api/v1/me/dsrBearer token

Submit GDPR data subject request

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."
}
POST/api/v1/me/parental-consent/resendBearer token

Re-send guardian consent email

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.

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).

Parameters, scopes and examples

Consent capture

Request body

{
  "consent_type": "photo_use",
  "granted": true,
  "organization_id": "uuid"
}
GET/api/v1/me/health-questionnaireBearer token

Health questionnaire completion status

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.

Parameters, scopes and examples

Questionnaire responses (7 keys)

Request body

{
  "responses": {
    "heart_condition": false,
    "pregnant": false,
    "blood_pressure": false,
    "medications": "",
    "injuries": "",
    "first_time_hot_yoga": true,
    "acknowledged_risks": true
  }
}
GET/api/v1/me/calendar-feedBearer token

Calendar feed state

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).

Parameters, scopes and examples

Response example

{
  "data": {
    "token": "r4nd0m_base64url_token",
    "enabled": true,
    "generatedAt": "2026-06-26T10:00:00.000Z"
  },
  "error": null
}
POST/api/v1/me/calendar-feedBearer token

Enable calendar feed

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).

Parameters, scopes and examples

Response example

{
  "data": {
    "token": "r4nd0m_base64url_token",
    "feedUrl": null,
    "enabled": true,
    "generatedAt": "2026-06-26T10:00:00.000Z"
  },
  "error": null
}
DELETE/api/v1/me/calendar-feedBearer token

Revoke calendar feed

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.

Parameters, scopes and examples

Path parameters

tokenstring · required
Opaque per-user feed token

Response example

{
  "data": {
    "bookings": [
      {
        "id": "uuid",
        "classInstanceId": "uuid",
        "title": "Vinyasa Flow",
        "description": "Instructor: Jane Doe",
        "location": {
          "name": "Studio 1",
          "address": "Studio Lane 24"
        },
        "startAt": "2026-06-27T09:00:00.000Z",
        "endAt": "2026-06-27T10:00:00.000Z",
        "status": "confirmed",
        "instructor": "Jane Doe",
        "room": "Studio 1",
        "organizationName": "Harbor Movement",
        "cancelUrl": "https://harbor-movement.example/bookings/uuid"
      }
    ],
    "cancellations": [],
    "userId": "uuid",
    "generatedAt": "2026-06-26T10:00:00.000Z"
  },
  "error": null
}
GET/api/v1/me/checkout/previewBearer token

Checkout pricing preview

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.

Parameters, scopes and examples

Artifact acceptance

Request body

{
  "artifact_kind": "contract",
  "document_id": "uuid",
  "version_id": "uuid:2",
  "signature_data": "data:image/png;base64,…",
  "pass_type_slug": "monthly-membership",
  "start_date": "2026-09-01"
}
GET/api/v1/me/guest-invitesBearer token

Can I bring a guest to this class?

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.

Parameters, scopes and examples

Query parameters

class_instance_idstring · required
Class to check

Response example

{
  "data": {
    "eligible": true,
    "reason": null,
    "reason_code": null,
    "price": 149,
    "compare_at_price": 275,
    "savings_percent": 46,
    "currency": "DKK",
    "class": {
      "id": "uuid",
      "className": "Hot Yoga",
      "startTime": "2026-08-12T17:00:00Z"
    },
    "invites": []
  },
  "error": null
}
POST/api/v1/me/guest-invitesBearer token

Invite a guest to a class

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.

Parameters, scopes and examples

Guest invitation

Request body

{
  "class_instance_id": "uuid",
  "guest_name": "Alex Friend",
  "guest_email": "alex@example.com",
  "payer": "guest",
  "return_base_url": "https://hotyogacph.dk"
}
DELETE/api/v1/me/guest-invites/{id}Bearer token

Withdraw a guest invitation

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.

Parameters, scopes and examples

Checkout options

Request body

{
  "return_base_url": "https://hotyogacph.dk",
  "legal_acceptances": []
}
POST/api/v1/guest-invites/{token}/waitlistPublic

Join the waiting list for a full class

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 }`.

POST/api/v1/guest-invites/{token}/claimBearer token

Claim a guest invitation

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.

Parameters, scopes and examples

Claim options

Request body

{
  "return_base_url": "https://hotyogacph.dk",
  "legal_acceptances": []
}
Appointments8 documented operations
GET/api/v1/appointmentsBearer token

List my appointments

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.

Parameters, scopes and examples

Path parameters

idstring · required
Appointment UUID

Caller-rendered concurrency snapshot

Request body

{
  "expected_updated_at": "2026-08-28T09:15:30.000Z",
  "reason": "Plans changed"
}
GET/api/v1/me/appointments/{id}/cancellation-previewBearer token

Preview my appointment cancellation

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.

Response example

{
  "data": {
    "service_id": "uuid",
    "provider_id": "uuid",
    "currency": "DKK",
    "price_amount": 500,
    "deposit_amount": 100,
    "amount_due_at_booking": 100,
    "outstanding_after_booking": 400,
    "payment_required": true,
    "payment_timing": "at_booking",
    "payment_at_booking_mode": "online",
    "payment_checkout_available": true
  }
}
POST/api/v1/appointments/checkoutBearer token

Prepare paid appointment checkout

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.

Request body

{
  "service_id": "uuid",
  "provider_id": "uuid",
  "start_time": "2026-08-07T10:00:00.000Z",
  "location_id": "uuid"
}

Response example

{
  "data": {
    "operation_id": "uuid",
    "status": "payment_pending",
    "client_secret": "pi_xxx_secret_xxx",
    "customer_id": "cus_xxx",
    "ephemeral_key": "ek_xxx",
    "stripe_account_id": null,
    "merchant_country_code": "DK",
    "regional_revision": "org:venue-id:r4"
  }
}
POST/api/v1/appointments/checkout/confirmBearer token

Finalize paid appointment

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.

Parameters, scopes and examples

Durable appointment checkout operation

Request body

{
  "operation_id": "uuid"
}

Response example

{
  "data": {
    "booked": true,
    "status": "applied",
    "operation_id": "uuid",
    "appointment": {
      "appointment_id": "uuid",
      "status": "confirmed"
    }
  }
}
Courses5 documented operations
GET/api/v1/admin/courses/{courseId}/booking-accessAPI key

Get a cohort booking allowance

Returns the venue-scoped class and optional-workshop resources, booking limits, and validity windows granted to participants in one course cohort.

Parameters, scopes and examples

Required scopes

read:courses

Path parameters

courseIdstring · required
Course cohort UUID
PATCH/api/v1/admin/courses/{courseId}/booking-accessAPI key

Update a cohort booking allowance

Atomically resolves all selected resources within the API-key venue, updates independent class/workshop allowance buckets, and reconciles active participants.

Parameters, scopes and examples

Required scopes

write:courses

Path parameters

courseIdstring · required
Course cohort UUID
POST/api/v1/admin/courses/{courseId}/booking-access/overridesAPI key

Override participant booking allowances

Applies or resets class/workshop limits and validity windows for one or many participants in the same tenant-scoped course cohort.

Parameters, scopes and examples

Required scopes

write:courses

Path parameters

courseIdstring · required
Course cohort UUID
POST/api/v1/courses/managed-by-pass/application-enrollmentAPI key

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.

Parameters, scopes and examples

Required scopes

write:members

Accepted external course application

Request body

{
  "pass_type_id": "00000000-0000-4000-8000-000000000101",
  "application_id": "YB-260806-1234",
  "email": "synthetic.applicant@example.com",
  "first_name": "Synthetic",
  "last_name": "Applicant",
  "phone": "+4511111111",
  "track_name": "Weekday Program",
  "payment_choice": "paid_deposit"
}

Response example

{
  "data": {
    "created": true,
    "enrollment_id": "00000000-0000-4000-8000-000000000102",
    "course_id": "00000000-0000-4000-8000-000000000103",
    "track_id": "00000000-0000-4000-8000-000000000104",
    "track_name": "Weekday Program",
    "payment_status": "unpaid",
    "booked_sessions": 18,
    "total_sessions": 18
  },
  "error": null
}
POST/api/v1/courses/{id}/purchaseBearer token

Buy a paid course

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`).

Parameters, scopes and examples

Path parameters

idstring · required
Course id

Response example

{
  "data": {
    "client_secret": "pi_xxx_secret_xxx",
    "customer_id": "cus_xxx",
    "ephemeral_key": "ek_test_xxx",
    "stripe_account_id": null,
    "merchant_country_code": "DK",
    "regional_revision": "org:venue-id:r4",
    "payment_intent_id": "pi_xxx",
    "enrollment_id": "uuid",
    "amount": 1500,
    "currency": "DKK"
  },
  "error": null
}
Admin128 documented operations
GET/api/v1/admin/dashboardBearer or API key

Dashboard stats

Today, weekly, and monthly KPIs: bookings, revenue, members, capacity, churn, MRR.

Parameters, scopes and examples

Required scopes

read:reports
GET/api/v1/admin/reports/brandsBearer or API key

Per-brand comparison report

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.

Parameters, scopes and examples

Appointment booking

Request body

{
  "service_id": "uuid",
  "provider_id": "uuid",
  "start_time": "2026-08-01T12:00:00Z",
  "client_id": "uuid",
  "source": "phone",
  "notification_channels": []
}
GET/api/v1/admin/appointments/{id}Bearer token

Appointment operational detail

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

Request body

{
  "action": "cancel",
  "expected_updated_at": "2026-08-28T09:15:30.000Z",
  "reason": "Client requested",
  "notification_channels": []
}
POST/api/v1/admin/appointments/{id}/historical-correctionsBearer token

Correct a historical appointment record

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

Request body

{
  "operation": "appointment.correct_attendance_state",
  "expected_updated_at": "2026-08-28T09:15:30.000Z",
  "expected_status": "confirmed",
  "history_reason": "Signed appointment record confirms this correction",
  "history_confirmation_token": "REWRITE",
  "effective_at": "2026-08-27T11:00:00.000Z",
  "intent": {
    "status": "completed",
    "checkedInAt": "2026-08-27T10:00:00.000Z",
    "completedAt": "2026-08-27T11:00:00.000Z"
  }
}
GET/api/v1/admin/reviewsBearer token

Venue review inbox

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.

PATCH/api/v1/admin/reviews/{sourceType}/{id}Bearer token

Moderate a venue review

Publishes or unpublishes one tenant-bound class or appointment review. Permission: feedback.manage. Idempotency-Key required.

Parameters, scopes and examples

Path parameters

sourceTypestring · required
class | appointment
idstring · required
Review UUID
POST/api/v1/admin/marketing/audience-previewBearer token

Preview a mobile flash-sale audience

Returns the consent-, preference-, contact-, and suppression-aware email/SMS reach for a venue-scoped marketing audience. Permission: marketing.flash_sales.

GET/api/v1/admin/payments/{id}/refund/reviewBearer token

Load refund dialog context

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.

Parameters, scopes and examples

Required scopes

billing.refunds.same_daybilling.refunds.full

Path parameters

idstring · required
Payment UUID
POST/api/v1/admin/payments/{id}/refund/reviewBearer token

Create immutable refund review

Idempotency-Key required. Body uses major units: { amount, reason? (staff-only), client_receipt_comment? (client-visible), destination_id? (original or venue method id), method_reference?, guest_booking_id? }. Returns the immutable review fields and full-refund confirmation phrase.

Parameters, scopes and examples

Required scopes

billing.refunds.same_daybilling.refunds.full

Path parameters

idstring · required
Payment UUID
GET/api/v1/admin/marketing/flash-salesBearer token

Venue flash-sale operations

Recent flash sales with promo code, lifecycle, linked campaign, and delivery outcomes for the Business app. Permission: marketing.flash_sales.

POST/api/v1/admin/marketing/flash-salesBearer token

Create and announce a flash sale

Creates a venue-scoped promo and public offer, optionally dispatching a consent-gated email/SMS campaign. Idempotency-Key required. Permission: marketing.flash_sales.

PATCH/api/v1/admin/marketing/flash-sales/{id}Bearer token

Deactivate a flash sale

Deactivates the sale and its linked promo code. Idempotency-Key required. Permission: marketing.flash_sales.

Parameters, scopes and examples

Path parameters

idstring · required
Flash sale UUID
GET/api/v1/admin/marketing/campaignsBearer token

Recent campaign outcomes

Venue-scoped campaign lifecycle and delivery metrics for the Business app. Permission: marketing.campaigns.

GET/api/v1/admin/marketing/review-promptsBearer token

Review-request delivery activity

Explains current automatic review-request rules and returns anonymized recent delivery outcomes. Permission: marketing.reviews.

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.

Parameters, scopes and examples

Required scopes

sites.manage
GET/api/v1/admin/pass-types/clips-empty-offersBearer token

List extra-class offer configuration

Returns active limited pass types, eligible target pass types (including hidden staff-only items), and the current exhausted-credit offer mappings. Permission: passes.manage.

Parameters, scopes and examples

Required scopes

passes.manage
PATCH/api/v1/admin/pass-types/clips-empty-offersBearer token

Configure an extra-class offer

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.

Parameters, scopes and examples

Required scopes

passes.manage

Source pass and nullable offer target

Request body

{
  "source_pass_type_id": "00000000-0000-4000-8000-000000000001",
  "target_pass_type_id": "00000000-0000-4000-8000-000000000002"
}
GET/api/v1/admin/pos/pass-typesBearer token

List the staff POS pass catalog

Returns every active venue pass type for authenticated POS staff, including pass types intentionally hidden from public consumer catalogs. Permission: pos.access.

Parameters, scopes and examples

Required scopes

pos.access
GET/api/v1/admin/checkin/{classInstanceId}/streamBearer token

Class stream controls

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.

Parameters, scopes and examples

Required scopes

booking.checkinschedule.view_allclass.createstaff_portal.roster.view

Path parameters

classInstanceIdstring · required
Class instance UUID
PATCH/api/v1/admin/checkin/{classInstanceId}/stream/sourceBearer token

Select class stream source

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.

Parameters, scopes and examples

Required scopes

class.create

Path parameters

classInstanceIdstring · required
Class instance UUID

Occurrence-level source selection

Request body

{
  "stream_source_id": "00000000-0000-4000-8000-000000000001"
}
POST/api/v1/admin/checkin/{classInstanceId}/stream/go-liveBearer token

Start class livestream

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.

Parameters, scopes and examples

Required scopes

class.create

Path parameters

classInstanceIdstring · required
Class instance UUID

Optional explicit source for this go-live attempt

Request body

{
  "stream_source_id": "00000000-0000-4000-8000-000000000001"
}
POST/api/v1/admin/checkin/{classInstanceId}/stream/endBearer token

End class livestream

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.

Parameters, scopes and examples

Required scopes

class.create

Path parameters

classInstanceIdstring · required
Class instance UUID
POST/api/v1/admin/checkin/{classInstanceId}/stream/retry-provider-shutdownBearer token

Retry completed class provider shutdown

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.

Parameters, scopes and examples

Required scopes

class.create

Path parameters

classInstanceIdstring · required
Class instance UUID
POST/api/v1/admin/checkin/{classInstanceId}/stream/phone-publisherBearer token

Prepare phone publisher session

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.

Parameters, scopes and examples

Required scopes

class.create

Path parameters

classInstanceIdstring · required
Class instance UUID

Optional device label shown to staff

Request body

{
  "device_label": "Front-desk iPhone"
}
POST/api/v1/admin/checkin/{classInstanceId}/stream/phone-publisher/claimBearer token

Claim phone publisher credential (one-time)

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.

Parameters, scopes and examples

Required scopes

class.create

Path parameters

classInstanceIdstring · required
Class instance UUID

One-time claim token from prepare

Request body

{
  "claim_token": "base64url-opaque-token",
  "device_label": "Front-desk iPhone"
}
POST/api/v1/admin/checkin/{classInstanceId}/stream/phone-publisher/startBearer token

Report phone publisher started

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.

Parameters, scopes and examples

Required scopes

class.create

Path parameters

classInstanceIdstring · required
Class instance UUID
POST/api/v1/admin/checkin/{classInstanceId}/stream/phone-publisher/heartbeatBearer token

Phone publisher heartbeat/status

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.

Parameters, scopes and examples

Required scopes

class.create

Path parameters

classInstanceIdstring · required
Class instance UUID
POST/api/v1/admin/checkin/{classInstanceId}/stream/phone-publisher/endBearer token

End phone publisher session

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.

Parameters, scopes and examples

Required scopes

class.create

Path parameters

classInstanceIdstring · required
Class instance UUID
POST/api/v1/admin/checkin/{classInstanceId}/stream/phone-publisher/revokeBearer token

Revoke phone publisher session

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.

Parameters, scopes and examples

Required scopes

class.create

Path parameters

classInstanceIdstring · required
Class instance UUID
POST/api/v1/admin/checkin/{classInstanceId}/stream/phone-publisher/retry-cleanupBearer token

Retry phone publisher provider cleanup

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.

Parameters, scopes and examples

Required scopes

write:schedule

Path parameters

idstring · required
Class instance ID

Class instance patch

Request body

{
  "start_time": "2026-07-10T08:00:00+02:00",
  "end_time": "2026-07-10T09:00:00+02:00",
  "instructor_id": "uuid",
  "capacity": 24,
  "notify": {
    "audience": {
      "clients": true
    },
    "channels": {
      "email": true,
      "sms": false
    }
  }
}
POST/api/v1/admin/schedule/{id}/historical-correctionsBearer token

Correct an isolated past class instance

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"
  }
}
POST/api/v1/admin/staff/{staffId}/availability/windows/{id}/historical-correctionsBearer token

Correct a staff availability history window

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.

Parameters, scopes and examples

Required scopes

availability.manage_history

Path parameters

staffIdstring · required
Availability owner user ID
idstring · required
Availability window ID

Bounded historical availability correction

Request body

{
  "operation": "availability_window.correct_effective_period",
  "expected_updated_at": "2026-08-20T09:00:00.000Z",
  "expected_is_active": true,
  "history_reason": "Approved rota shows this earlier effective period",
  "history_confirmation_token": "REWRITE",
  "effective_at": "2026-08-10T10:00:00.000Z",
  "intent": {
    "effectiveFrom": "2026-08-01",
    "effectiveUntil": "2026-08-15"
  }
}
POST/api/v1/admin/schedule/bulk-editBearer token

Edit selected class instances

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.

Parameters, scopes and examples

Required scopes

write:schedule

Selected class edits

Request body

{
  "instance_ids": [
    "uuid"
  ],
  "set": {
    "room_id": "uuid",
    "capacity": 24
  },
  "time": {
    "start": "09:00",
    "end": "10:00",
    "timezone": "Europe/Copenhagen"
  },
  "notify": {
    "audience": {
      "clients": true
    },
    "channels": {
      "email": true,
      "sms": false,
      "push": true
    }
  }
}
POST/api/v1/admin/schedule/bulk-cancelBearer token

Cancel selected class instances

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.

Parameters, scopes and examples

Required scopes

write:schedule

Selected class cancellations

Request body

{
  "instance_ids": [
    "uuid"
  ],
  "reason": "Instructor unavailable",
  "notify": {
    "audience": {
      "clients": true,
      "instructor": true
    },
    "channels": {
      "email": true,
      "sms": false,
      "push": true
    }
  }
}
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, 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
POST/api/v1/admin/checkin/{classInstanceId}/contactBearer token

Bulk email/SMS a class roster

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. 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
pageinteger
Page numberDefault: 1
limitinteger
Items per pageDefault: 20

Response example

{
  "data": [],
  "error": null,
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 0,
    "has_more": false,
    "counts": {
      "all": 0,
      "active": 0,
      "inactive": 0,
      "new": 0,
      "no_pass": 0
    },
    "pass_type_options": [
      {
        "id": "00000000-0000-4000-8000-000000000101",
        "name": "Unlimited membership",
        "family": "recurring",
        "active_client_count": 24
      },
      {
        "id": "00000000-0000-4000-8000-000000000102",
        "name": "Course preparation pass",
        "family": "class_pack",
        "active_client_count": 3
      }
    ],
    "pass_family_options": [
      {
        "key": "recurring",
        "active_client_count": 24
      },
      {
        "key": "class_pack",
        "active_client_count": 3
      },
      {
        "key": "time_based",
        "active_client_count": 0
      },
      {
        "key": "intro_offer",
        "active_client_count": 0
      }
    ]
  }
}
POST/api/v1/admin/membersBearer token

Create or invite a client

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.

Parameters, scopes and examples

Required scopes

write:members

Discriminated client create request

Request body

{
  "mode": "register",
  "first_name": "Mia",
  "last_name": "Member",
  "email": "mia@example.com",
  "phone": "+4512345678"
}
GET/api/v1/admin/members/{id}Bearer or API key

Member detail

Full member profile: passes with course-fulfillment provenance, canonical native course_access, recent bookings/payments, tags, scores, credits, referrals, plus server-authoritative total_bookings and last_visit_at, per-channel notification_availability (email/SMS/push with exact unavailable reasons), and contact_details_visible (contact fields are nulled unless the caller holds members.contact and membership contact visibility).

Parameters, scopes and examples

Required scopes

read:members

Path parameters

idstring · required
Member user ID
POST/api/v1/admin/members/{id}/deactivateBearer token

Deactivate a client in this venue

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

Request body

{
  "reason": "Client requested account closure",
  "notify": {
    "audience": {
      "clients": true
    },
    "channels": {
      "email": true,
      "push": false
    }
  }
}
GET/api/v1/admin/members/{id}/passesBearer token

Paginated member pass history

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.

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
GET/api/v1/admin/members/{id}/bookingsBearer token

Paginated member booking and visit history

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.

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)
GET/api/v1/admin/members/{id}/paymentsBearer token

Paginated member payment 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
GET/api/v1/admin/members/{id}/payments/{paymentId}/receiptBearer token

Download a member payment receipt

Streams the canonical venue-branded PDF for a receipt-bearing payment after proving active membership and same-venue payment ownership.

Parameters, scopes and examples

Required scopes

members.view_insights

Path parameters

idstring · required
Active member ID
paymentIdstring · required
Same-venue payment ID
POST/api/v1/admin/members/{id}/payments/{paymentId}/receipt/sendBearer token

Re-send a POS payment receipt

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.

Parameters, scopes and examples

Required scopes

reports.view

Query parameters

rangestring
Venue-local window: 'today' | '7d' | '30d'Default: today
cursorstring
Opaque cursor from a previous page (next_cursor)
limitinteger
Page size (1–50)Default: 25
POST/api/v1/admin/payments/{id}/refundBearer token

Refund a payment

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.

Parameters, scopes and examples

Required scopes

billing.refunds.same_daybilling.refunds.full

Path parameters

idstring · required
Refund UUID
POST/api/v1/admin/checkin/{classInstanceId}/contactBearer token

Bulk email/SMS a class roster

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!"
}
GET/api/v1/admin/checkin/{classInstanceId}/guest-optionsBearer token

Guest payment options

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.

Parameters, scopes and examples

Required scopes

booking.checkin

Path parameters

classInstanceIdstring · required
Class instance UUID
POST/api/v1/admin/checkin/{classInstanceId}/guestsBearer token

Add guest spots

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

Request body

{
  "first_name": "Nora",
  "last_name": "Guest",
  "email": "nora@example.com",
  "number_of_guests": 2,
  "payment_mode": "send_payment_link",
  "payment_option_id": "00000000-0000-4000-8000-000000000001",
  "delivery_channels": [
    "email"
  ]
}
GET/api/v1/admin/billing/failed-paymentsBearer token

Org-wide failed payments

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
POST/api/v1/admin/billing/failed-payments/{paymentId}/retryBearer token

Retry a failed payment

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). Empty body. Idempotency-Key required. 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
POST/api/v1/admin/billing/failed-payments/{paymentId}/settle-externalBearer token

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
}
POST/api/v1/admin/billing/failed-payments/{paymentId}/waiveBearer token

Waive a failed renewal cycle

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
}
POST/api/v1/admin/passes/{passId}/suspendBearer token

Suspend (deactivate) a pass

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

Request body

{
  "reason": "Unpaid balance",
  "notify": {
    "audience": {
      "clients": true
    },
    "channels": {
      "email": true,
      "sms": false,
      "push": true
    }
  }
}
POST/api/v1/admin/passes/{passId}/unsuspendBearer token

Unsuspend a pass

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.

Parameters, scopes and examples

Required scopes

passes.manage

Path parameters

passIdstring · required
Pass UUID

Optional explicit client notification choice

Request body

{
  "notify": {
    "audience": {
      "clients": true
    },
    "channels": {
      "email": true,
      "sms": false,
      "push": false
    }
  }
}
POST/api/v1/admin/passes/{passId}/reactivateBearer token

Reactivate a terminal pass

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.

Parameters, scopes and examples

Required scopes

passes.manage

Path parameters

passIdstring · required
Pass UUID

Optional new end date for a run-out window

Request body

{
  "new_end_date": "2026-09-30",
  "notify": {
    "audience": {
      "clients": true
    },
    "channels": {
      "email": true,
      "sms": true,
      "push": false
    }
  }
}
GET/api/v1/admin/members/{id}/payment-methodsBearer token

Selected-client saved-card availability

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 the canonical pos.access permission. Never returns customer IDs, processor metadata, full card data, or client secrets.

Parameters, scopes and examples

Required scopes

pos.access

Path parameters

idstring · required
Active member user ID

Response example

{
  "payment_methods": [
    {
      "id": "pm_example",
      "brand": "visa",
      "last4": "4242",
      "exp_month": 12,
      "exp_year": 2030,
      "is_default": true,
      "chargeable": true,
      "expired": false
    }
  ],
  "has_chargeable_card": true
}
POST/api/v1/admin/members/{id}/payment-methods/setup-intentBearer token

Collect a client card in person

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.

Parameters, scopes and examples

Required scopes

members.contact

Path parameters

idstring · required
Active member user ID

Client-present consent attestation

Request body

{
  "consent_channel": "in_person"
}

Response example

{
  "data": {
    "client_secret": "seti_xxx_secret_xxx",
    "setup_intent_id": "seti_xxx",
    "stripe_account_id": null,
    "merchant_country_code": "DK",
    "regional_revision": "org:venue-id:r4"
  }
}
PATCH/api/v1/admin/members/{id}Bearer or API key

Update member contact details

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.

Parameters, scopes and examples

Required scopes

write:members

Path parameters

idstring · required
Member user ID

Credit adjustment

Request body

{
  "amount": 50,
  "currency": "DKK",
  "reason": "Goodwill",
  "notify": {
    "audience": {
      "clients": true
    },
    "channels": {
      "email": true
    }
  }
}
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.

Parameters, scopes and examples

Required scopes

write:bookings

Admin booking

Request body

{
  "user_id": "uuid",
  "class_instance_id": "uuid",
  "pass_id": "uuid",
  "override_capacity": false,
  "notify": {
    "audience": {
      "clients": true
    },
    "channels": {
      "email": true,
      "push": true
    }
  }
}
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.

Parameters, scopes and examples

Required scopes

write:bookings

Manual waitlist add

Request body

{
  "user_id": "uuid",
  "class_instance_id": "uuid",
  "attendance_type": "physical",
  "notify": {
    "audience": {
      "clients": true
    },
    "channels": {
      "email": true,
      "sms": false,
      "push": false
    }
  }
}
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.

Request body

{
  "reason": "Client requested removal",
  "notify": {
    "audience": {
      "clients": true
    },
    "channels": {
      "email": true,
      "sms": false,
      "push": true
    }
  }
}
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).

Parameters, scopes and examples

Required scopes

write:bookings

Path parameters

bookingIdstring · required
Confirmed booking ID

Cancel options

Request body

{
  "reason": "Client requested",
  "notify": {
    "audience": {
      "clients": true
    },
    "channels": {
      "email": true,
      "sms": false,
      "push": false
    }
  },
  "waive_fee": false
}
POST/api/v1/admin/bookings/{bookingId}/historical-correctionsBearer token

Correct a past class roster record

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.

Request body

{
  "operation": "class_booking.correct_attendance_state",
  "expected_updated_at": "2026-08-20T09:00:00.000Z",
  "expected_class_updated_at": "2026-08-20T09:00:00.000Z",
  "history_reason": "Signed paper roster confirms this correction",
  "history_confirmation_token": "REWRITE",
  "effective_at": "2026-08-20T10:00:00.000Z",
  "intent": {
    "status": "checked_in"
  }
}
POST/api/v1/admin/memberships/previewBearer token

Preview a staff-created recurring membership

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.

Request body

{
  "member_id": "uuid",
  "pass_type_id": "uuid",
  "start_date": "2026-08-20",
  "payment_timing": "on_start",
  "first_period_free": false,
  "waive_registration_fee": false,
  "registration_fee_discount": {
    "kind": "percent_off",
    "percent": 50
  },
  "saved_payment_method_id": "pm_…",
  "discount_schedule": {
    "duration_kind": "first_n_periods",
    "value_kind": "fixed_price",
    "value": 399,
    "periods": 3
  },
  "send_contract_and_terms": true
}
POST/api/v1/admin/membershipsBearer token

Create a recurring membership for a client

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

Request body

{
  "member_id": "uuid",
  "pass_type_id": "uuid",
  "payment_timing": "now",
  "first_period_free": true,
  "waive_registration_fee": true,
  "registration_fee_discount": {
    "kind": "percent_off",
    "percent": 100
  },
  "saved_payment_method_id": "pm_…",
  "send_contract_and_terms": true
}
GET/api/v1/admin/memberships/change-optionsBearer token

List membership change options for a client pass

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.

Parameters, scopes and examples

Required scopes

passes.manage

Accepted server quote binding

Request body

{
  "pass_id": "uuid",
  "target_pass_type_id": "uuid",
  "quote": {
    "fingerprint": "64-character SHA-256 hex",
    "issued_at": "2026-08-05T20:00:00.000Z",
    "expires_at": "2026-08-05T20:05:00.000Z",
    "override_amount_minor": null
  }
}
POST/api/v1/admin/pos/saleBearer or API key

POS sale

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.

Parameters, scopes and examples

Required scopes

write:pos

Sale

Request body

{
  "member_id": "uuid",
  "items": [
    {
      "type": "product",
      "product_id": "uuid",
      "quantity": 1
    }
  ],
  "payment_method": "card",
  "saved_card_id": "pm_…"
}

Response example

{
  "data": {
    "payment_status": "requires_action",
    "client_secret": "pi_xxx_secret_xxx",
    "payment_intent_id": "pi_xxx",
    "stripe_account_id": null,
    "merchant_country_code": "DK",
    "regional_revision": "org:venue-id:r4"
  }
}
GET/api/v1/admin/pos/recentBearer or API key

Recent POS transactions

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).

Parameters, scopes and examples

Required scopes

write:members

Path parameters

idstring · required
Member user ID or display ID

Pause window

Request body

{
  "pass_id": "uuid",
  "pause_start": "2026-08-01",
  "pause_end": "2026-08-21",
  "notify": {
    "audience": {
      "clients": true
    },
    "channels": {
      "email": true,
      "sms": false,
      "push": true
    }
  }
}
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.

Request body

{
  "pass_id": "uuid",
  "resume_from": "2026-09-15",
  "notify": {
    "audience": {
      "clients": true
    },
    "channels": {
      "email": true
    }
  }
}
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.

Parameters, scopes and examples

Required scopes

write:members

Path parameters

idstring · required
Member user ID or display ID

Termination request

Request body

{
  "pass_id": "uuid",
  "mode": "period_end",
  "reason": "Member request",
  "notify": {
    "audience": {
      "clients": true
    },
    "channels": {
      "email": true,
      "sms": true
    }
  }
}
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.

Parameters, scopes and examples

Required scopes

write:pos

Receipt

Request body

{
  "payment_intent_id": "pi_xxx",
  "method": "email",
  "recipient": "client@example.com"
}
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.

Parameters, scopes and examples

Invitation fields

Request body

{
  "email": "teacher@example.com",
  "role": "instructor",
  "first_name": "Anna",
  "last_name": "Jensen",
  "location_ids": []
}
GET/api/v1/admin/staff-scheduleBearer token

List all staff shifts

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.

Parameters, scopes and examples

Shift fields

Request body

{
  "staff_id": "uuid",
  "location_id": "uuid",
  "shift_type": "regular",
  "start_time": "2026-05-01T09:00:00Z",
  "end_time": "2026-05-01T17:00:00Z",
  "break_minutes": 30,
  "role_required": "reception",
  "hourly_rate": 200
}
POST/api/v1/admin/staff-schedule/{id}/assignBearer token

Assign a staff member to a shift

Permission: staff_scheduling.manage. Emits shift.assigned.

Parameters, scopes and examples

Path parameters

idstring · required
Shift ID

Staff to assign

Request body

{
  "staff_id": "uuid"
}
GET/api/v1/admin/walk-in-queueBearer or API key

Walk-in queue

Current walk-in queue (waiting + notified) for the authenticated org, ordered by position.

Parameters, scopes and examples

Required scopes

read:bookings
POST/api/v1/admin/walk-in-queueBearer or API key

Add walk-in

Add a walk-in to the queue. Allocates an atomic position via allocate_queue_position(). Idempotency-Key required.

Parameters, scopes and examples

Required scopes

write:bookings

Walk-in entry

Request body

{
  "customer_name": "Jane Doe",
  "customer_phone": "+4512345678",
  "party_size": 1,
  "service_requested": "Massage",
  "notify_sms": true
}
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.

Parameters, scopes and examples

Required scopes

write:checkins

Path parameters

classInstanceIdstring · required
Class instance UUID

Query parameters

refreshboolean
Force generate a new token (rotate)Default: false

Response example

{
  "data": {
    "token": "a1b2c3d4e5f6...64hex chars",
    "expires_at": "2026-06-01T10:05:00Z",
    "class_instance_id": "uuid"
  }
}
GET/api/v1/admin/ratesBearer or API key

Get floating-rate rules

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.

Parameters, scopes and examples

Required scopes

read:passes

Query parameters

pass_type_idstring · required
Pass type id

Response example

{
  "data": {
    "pass_type_id": "uuid",
    "graduation_tiers": [],
    "seasonal_rates": []
  },
  "error": null
}
GET/api/v1/admin/rates/impactBearer or API key

Preview a floating-rate rule impact

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.

Parameters, scopes and examples

Required scopes

read:passes

Query parameters

pass_type_idstring · required
Pass type id
rulestring · required
graduation or seasonal

Response example

{
  "data": {
    "affectedMembers": 4,
    "currentRevenue": 2000,
    "projectedRevenue": 2200
  },
  "error": null
}
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.

Parameters, scopes and examples

Required scopes

read:passes

Query parameters

pass_type_idstring · required
Pass type id

Response example

{
  "data": {
    "totalMRR": 0,
    "byBucket": []
  },
  "error": null
}
GET/api/v1/admin/rates/upcomingBearer or API key

List upcoming rate changes

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.

Parameters, scopes and examples

Required scopes

read:passes

Query parameters

days_aheadnumber
Window from 1 to 365 daysDefault: 30

Response example

{
  "data": {
    "days_ahead": 30,
    "changes": []
  },
  "error": null
}
GET/api/v1/admin/rates/previewBearer or API key

Preview a client 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. The client and pass type must both belong to that venue.

Parameters, scopes and examples

Required scopes

read:passes

Query parameters

user_idstring · required
Client user id
pass_type_idstring · required
Pass type id
effective_datestring
Optional ISO timestamp

Response example

{
  "data": {
    "baseRate": 500,
    "resolvedRate": 450
  },
  "error": null
}
POST/api/v1/admin/rates/overrideBearer or API key

Set 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

Rate override

Request body

{
  "pass_id": "uuid",
  "rate": 450,
  "reason": "Retention offer"
}

Response example

{
  "data": {
    "ok": true
  },
  "error": null
}
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.

Parameters, scopes and examples

Required scopes

read:private_events

Query parameters

statusstring
Optional booking status

Response example

{
  "data": [
    {
      "id": "uuid",
      "status": "inquiry"
    }
  ],
  "error": null
}
POST/api/v1/admin/private-eventsBearer or API key

Create 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

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.

Request body

{
  "event_type_id": "uuid",
  "start_time": "2026-07-20T16:00:00.000Z",
  "participant_count": 12,
  "contact_name": "Client",
  "contact_email": "client@example.test",
  "start_mode": "confirm_on_payment",
  "billing_target": "partner",
  "partner_id": "uuid",
  "pricing_override": {
    "mode": "per_person",
    "amount": 250
  }
}

Response example

{
  "data": {
    "id": "uuid",
    "status": "quoted",
    "client_id": null,
    "client_created": false,
    "payment_due_at": "2026-07-19T16:00:00.000Z"
  },
  "error": null
}
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.

Parameters, scopes and examples

Required scopes

read:private_events

Path parameters

idstring · required
Booking id

Response example

{
  "data": {
    "id": "uuid",
    "status": "inquiry"
  },
  "error": null
}
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.

Request body

{
  "participant_count": 14,
  "reprice": true
}

Response example

{
  "data": {
    "id": "uuid",
    "updated": [
      "participant_count",
      "total_amount"
    ],
    "changeSummary": {
      "changes": [
        {
          "field": "participant_count",
          "label": "Participants",
          "from": "12",
          "to": "14"
        }
      ],
      "clientVisible": true
    },
    "notifyIntent": {
      "requested": false,
      "channels": [
        "email"
      ],
      "willSend": false,
      "reason": "staff_opted_out"
    }
  },
  "error": null
}
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.

Parameters, scopes and examples

Required scopes

write:private_events

Path parameters

idstring · required
Booking id

Response example

{
  "data": {
    "id": "uuid",
    "status": "confirmed",
    "invoice_id": null
  },
  "error": null
}
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.

Parameters, scopes and examples

Required scopes

write:private_events

Path parameters

idstring · required
Booking id

Cancellation reason

Request body

{
  "reason": "Client request"
}

Response example

{
  "data": {
    "id": "uuid",
    "status": "cancelled",
    "fee": 250,
    "paidToDate": 500,
    "suggestedRefund": 250
  },
  "error": null
}
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.

Parameters, scopes and examples

Required scopes

write:private_events

Path parameters

idstring · required
Booking id

Quote values

Request body

{
  "total_amount": 2500,
  "valid_days": 7
}

Response example

{
  "data": {
    "id": "uuid",
    "status": "quoted"
  },
  "error": null
}
GET/api/v1/admin/loyalty-priceBearer or API key

Get the venue loyalty programme snapshot

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.

Parameters, scopes and examples

Required scopes

read:passes

Response example

{
  "data": {
    "enabled": true,
    "display_name": "Loyalty Price",
    "settings": {
      "program": {},
      "visibility": {},
      "recurring": {},
      "packs": {}
    },
    "status_counts": {
      "none": 0,
      "qualifying": 12,
      "active": 48,
      "grace": 3,
      "lapsed": 7
    },
    "loyal_clients": 51
  },
  "error": null
}
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`. 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
}

Response example

{
  "data": {
    "granted": true,
    "source_id": "uuid",
    "status": "active",
    "status_source": "manual_grant",
    "loyal_since": "2026-08-18",
    "grace_until": null,
    "window_closes_on": null
  },
  "error": null
}
Calendar1 documented operation
GET/api/v1/calendarBearer or API key

Unified calendar feed

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.

Parameters, scopes and examples

Required scopes

read:calendar

Query parameters

startstring · required
YYYY-MM-DD inclusive
endstring · required
YYYY-MM-DD inclusive
room_idstring
Restrict to one room
staff_idstring
Restrict to one staff member
location_idstring
Restrict to one location
brand_idstring
Restrict to one brand
sourcesstring
Comma-separated subset of: class, appointment, private_event, stream, blocked_time, unavailability, blackout, room_rental, maintenance, staff_shift, open_gym
only_blockingstring
true to return only events with is_blocking=true
Maintenance2 documented operations
GET/api/v1/maintenanceAPI key

List maintenance slots

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).

Parameters, scopes and examples

Required scopes

read:maintenance

Query parameters

startstring
ISO datetime, inclusive lower bound on start_time
endstring
ISO datetime, inclusive upper bound on start_time
room_idstring
Restrict to one room
statusstring
scheduled | in_progress | completed | overdue | cancelled | deferred
limitinteger
Max rows (1-200)Default: 50
POST/api/v1/maintenanceAPI key

Schedule maintenance

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.

Parameters, scopes and examples

Path parameters

idstring · required
Class instance ID
POST/api/v1/staff/classes/{id}/roster/{bookingId}/historical-correctionsBearer token

Correct a past class roster record

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

Request body

{
  "operation": "class_booking.correct_attendance_state",
  "expected_updated_at": "2026-08-20T09:00:00.000Z",
  "expected_class_updated_at": "2026-08-20T09:00:00.000Z",
  "history_reason": "Signed paper roster confirms this correction",
  "history_confirmation_token": "REWRITE",
  "effective_at": "2026-08-20T10:00:00.000Z",
  "intent": {
    "status": "checked_in"
  }
}
POST/api/v1/staff/classes/{id}/historical-correctionsBearer token

Correct an assigned past class instance

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.

Parameters, scopes and examples

Path parameters

idstring · required
Class instance ID

Provider-scoped historical class-instance correction

Request body

{
  "operation": "class_instance.correct_lifecycle_state",
  "expected_updated_at": "2026-08-20T09:00:00.000Z",
  "expected_status": "scheduled",
  "history_reason": "Signed teaching log confirms that this class was completed",
  "history_confirmation_token": "REWRITE",
  "effective_at": "2026-08-20T10:00:00.000Z",
  "intent": {
    "status": "completed"
  }
}
GET/api/v1/staff/coursesBearer token

List courses (staff)

Courses in the venue with enrolled/capacity counts and the next session. Admin/manager/reception see all; instructors only the courses they staff.

GET/api/v1/staff/courses/{id}Bearer token

Course detail + roster (staff)

Course facts, roster (track chips, payment status, attendance), sessions, and the caller’s per-course permissions.

Parameters, scopes and examples

Path parameters

idstring · required
Course ID
POST/api/v1/staff/courses/{id}/sessions/{sessionId}/attendanceBearer token

Mark course-session attendance

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.

Parameters, scopes and examples

Path parameters

idstring · required
Course ID
sessionIdstring · required
Class instance (session) ID

Attendance mark

Request body

{
  "user_id": "00000000-0000-0000-0000-000000000aaa",
  "present": true
}
POST/api/v1/staff/courses/{id}/messageBearer token

Message course participants

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.

Parameters, scopes and examples

Availability

Request body

{
  "unavailable_dates": [
    {
      "date": "2026-04-20",
      "reason": "Vacation"
    },
    {
      "date": "2026-04-21",
      "reason": "Vacation"
    }
  ]
}
DELETE/api/v1/staff/availabilityBearer token

Remove an unavailable date

Remove one unavailable date owned by the calling staff member. Permission: staff_portal.availability.

Parameters, scopes and examples

Query parameters

datestring
Unavailable date to remove (YYYY-MM-DD)
GET/api/v1/staff/availability/windowsBearer token

List recurring availability windows

Calling user's active recurring availability windows. Each row carries day_of_week, start_time, end_time, location_id, recurrence_type, recurrence_interval, and effective_from/until.

Parameters, scopes and examples

Query parameters

include_historicalstring
Include inactive/protected history; requires availability.manage_historyDefault: false
POST/api/v1/staff/availability/windowsBearer token

Create a recurring availability window

Create a new recurring availability window for the calling user. Body: day_of_week (0=Sun..6=Sat), start_time, end_time, optional location_id, recurrence_type (weekly|biweekly|triweekly|custom), recurrence_interval (1..12), effective_from, effective_until, notes.

Parameters, scopes and examples

Window payload

Request body

{
  "day_of_week": 1,
  "start_time": "09:00:00",
  "end_time": "12:00:00",
  "location_id": null,
  "recurrence_type": "weekly",
  "recurrence_interval": 1
}
PATCH/api/v1/staff/availability/windows/{id}Bearer token

Update a recurring availability window

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

Request body

{
  "expected_updated_at": "2026-08-28T09:15:30.000Z",
  "start_time": "10:00:00"
}
DELETE/api/v1/staff/availability/windows/{id}Bearer token

Soft-delete a recurring availability window

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.

Parameters, scopes and examples

Path parameters

idstring · required
Window ID

Caller-rendered concurrency snapshot

Request body

{
  "expected_updated_at": "2026-08-28T09:15:30.000Z"
}
POST/api/v1/staff/availability/windows/{id}/historical-correctionsBearer token

Correct my availability history window

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

Request body

{
  "action": "cancel",
  "expected_updated_at": "2026-08-28T09:15:30.000Z",
  "reason": "Client requested",
  "notification_channels": []
}
POST/api/v1/staff/appointments/{id}/historical-correctionsBearer token

Correct my assigned historical appointment

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

Request body

{
  "operation": "appointment.correct_no_show_state",
  "expected_updated_at": "2026-08-28T09:15:30.000Z",
  "expected_status": "confirmed",
  "history_reason": "Signed appointment record confirms this correction",
  "history_confirmation_token": "REWRITE",
  "effective_at": "2026-08-27T11:00:00.000Z",
  "intent": {
    "status": "no_show"
  }
}
GET/api/v1/staff/shiftsBearer token

My staff shifts

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).

Parameters, scopes and examples

Required scopes

write:staff

Shift creation payload

Request body

{
  "start_time": "2026-05-01T09:00:00Z",
  "end_time": "2026-05-01T17:00:00Z",
  "staff_id": "uuid",
  "shift_type": "regular",
  "break_minutes": 30,
  "role_required": "reception"
}
GET/api/v1/staff/shifts/{id}Bearer or API key

Get a staff shift

Fetch a single staff shift by id. JWT for the assignee or admin/manager, or API key with read:staff scope.

Parameters, scopes and examples

Required scopes

read:staff

Path parameters

idstring · required
Shift UUID
PATCH/api/v1/staff/shifts/{id}Bearer or API key

Update a staff shift

Partially update a shift. JWT (admin/manager) or API key with write:staff. Re-assigning staff_id emits shift.assigned.

Parameters, scopes and examples

Required scopes

write:staff

Path parameters

idstring · required
Shift UUID

Partial shift update

Request body

{
  "start_time": "2026-05-01T10:00:00Z",
  "staff_id": "uuid"
}
DELETE/api/v1/staff/shifts/{id}Bearer or API key

Cancel a staff shift

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.

Parameters, scopes and examples

Time-off request

Request body

{
  "start_date": "2026-05-12",
  "end_date": "2026-05-19",
  "time_off_type": "vacation",
  "notes": "Family trip"
}
Leads2 documented operations
POST/api/v1/leadsPublic

Capture a lead

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.

Request body

{
  "email": "jane@example.com",
  "first_name": "Jane",
  "last_name": "Doe",
  "phone": "+4512345678",
  "source": "website_form",
  "utm_source": "meta",
  "utm_medium": "cpc",
  "utm_campaign": "spring_2026",
  "utm_content": "hero_banner",
  "utm_term": "pilates copenhagen",
  "fbclid": "abc123",
  "landing_page_url": "https://harbor-movement.example/pricing",
  "referrer_url": "https://facebook.com",
  "metadata": {
    "form_id": "newsletter_signup"
  }
}

Response example

{
  "data": {
    "id": "uuid",
    "email": "jane@example.com",
    "source": "website_form",
    "status": "new",
    "created_at": "2026-04-17T12:00:00Z"
  },
  "error": null
}
GET/api/v1/leadsAPI key

List leads

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.

Request body

{
  "event_name": "purchase",
  "user_id": "uuid",
  "email": "jane@example.com",
  "value": 599,
  "currency": "DKK",
  "transaction_id": "pi_abc123",
  "utm_source": "meta",
  "utm_medium": "cpc",
  "utm_campaign": "spring_2026",
  "fbclid": "abc123",
  "page_url": "https://harbor-movement.example/pricing",
  "properties": {
    "content_name": "Unlimited Monthly"
  }
}

Response example

{
  "data": {
    "id": "uuid",
    "event_name": "purchase",
    "created_at": "2026-04-17T12:00:00Z"
  },
  "error": null
}
GET/api/v1/events/{id}Bearer or API key

Community event detail

Returns a single community event with RSVP going-count and the caller’s own RSVP. Accepts a booking id or the event-type slug.

Parameters, scopes and examples

Path parameters

idstring · required
Event booking id or event-type slug

Response example

{
  "data": {
    "id": "uuid",
    "title": "Summer Social",
    "start_time": "iso",
    "going_count": 12,
    "my_rsvp": null
  },
  "error": null
}
POST/api/v1/events/{id}/rsvpBearer or API key

RSVP to a community event

Creates or updates the caller’s RSVP for a community event.

Parameters, scopes and examples

Path parameters

idstring · required
Event booking id

Status + optional guest count + notes.

Request body

{
  "status": "going",
  "guest_count": 1,
  "notes": "Bringing my partner"
}

Response example

{
  "data": {
    "id": "uuid",
    "status": "going"
  },
  "error": null
}
Open Gym4 documented operations
POST/api/v1/open-gym/checkinBearer or API key

Check in to open gym

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.

Request body

{
  "location_id": "uuid",
  "source": "qr"
}

Response example

{
  "data": {
    "id": "uuid",
    "user_id": "uuid",
    "checked_in_at": "2026-04-18T10:00:00Z",
    "source": "qr"
  },
  "error": null
}
POST/api/v1/open-gym/checkoutBearer or API key

End an open-gym session

Mark an active open-gym session as checked out.

Parameters, scopes and examples

Required scopes

write:bookings

Session id to close.

Request body

{
  "session_id": "uuid"
}

Response example

{
  "data": {
    "ok": true
  },
  "error": null
}
GET/api/v1/open-gym/activeBearer or API key

List active open-gym sessions

Returns all currently checked-in open-gym sessions for the authenticated org.

Parameters, scopes and examples

Required scopes

read:bookings

Query parameters

location_idstring
Filter to a specific location

Response example

{
  "data": [
    {
      "id": "uuid",
      "user_id": "uuid",
      "checked_in_at": "iso"
    }
  ],
  "error": null
}
GET/api/v1/open-gym/statusBearer token

My open-gym status

Returns whether open gym is enabled for the org and the caller’s active (not-yet-checked-out) session, if any. Requires a user token.

Parameters, scopes and examples

Response example

{
  "data": {
    "enabled": true,
    "checked_in": true,
    "active_session": {
      "id": "uuid",
      "checked_in_at": "iso"
    }
  },
  "error": null
}
Waivers3 documented operations
GET/api/v1/waivers/requiredBearer token

List waivers the client must sign

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.

Parameters, scopes and examples

Response example

{
  "data": [
    {
      "template_id": "uuid",
      "slug": "liability",
      "title": "Liability waiver",
      "body_md": "# Liability waiver v2",
      "body": "# Liability waiver v2",
      "version": 2,
      "latest_signed_version": 1,
      "needs_sign": true
    }
  ],
  "error": null
}
GET/api/v1/waivers/{templateId}Public

Waiver template detail

Returns a single active waiver template (title + markdown body + version) for display before signing. Public-readable, IP-throttled.

Parameters, scopes and examples

Path parameters

templateIdstring · required
Waiver template id

Response example

{
  "data": {
    "id": "uuid",
    "slug": "liability-2026",
    "title": "Liability waiver",
    "body_md": "# ...",
    "version": 2
  },
  "error": null
}
POST/api/v1/waivers/signBearer token

Sign a waiver

Submit the authenticated client’s signature for a waiver template in the validated X-Organization-ID venue. Idempotent via Idempotency-Key.

Parameters, scopes and examples

Template id + typed name + optional signature SVG / minor guardian fields.

Request body

{
  "template_id": "uuid",
  "typed_name": "John Doe",
  "signature_svg": "<svg/>"
}

Response example

{
  "data": {
    "id": "uuid",
    "template_id": "uuid",
    "signed_at": "iso",
    "version": 2
  },
  "error": null
}
Discounts2 documented operations
GET/api/v1/discounts/verificationsBearer token

List my discount verifications

Returns the authenticated client’s submitted discount verifications and their status.

Parameters, scopes and examples

Response example

{
  "data": [
    {
      "id": "uuid",
      "type_slug": "student",
      "status": "approved",
      "expires_at": "iso"
    }
  ],
  "error": null
}
POST/api/v1/discounts/verificationsBearer token

Submit a discount verification

Submit proof for a discount eligibility type. Email-domain and DOB-rule types auto-approve.

Parameters, scopes and examples

Eligibility type slug and optional document URL.

Request body

{
  "type_slug": "student",
  "document_url": "https://..."
}

Response example

{
  "data": {
    "id": "uuid",
    "status": "pending"
  },
  "error": null
}
Challenges4 documented operations
GET/api/v1/challengesBearer or API key

List active challenges

Returns all active challenges for the current venue.

Parameters, scopes and examples

Response example

{
  "data": [
    {
      "id": "uuid",
      "name": "April 30",
      "kind": "individual"
    }
  ],
  "error": null
}
GET/api/v1/challenges/{id}Bearer or API key

Challenge detail

Returns a single active challenge with participant count and the caller’s own enrollment (if authed as a user).

Parameters, scopes and examples

Path parameters

idstring · required
Challenge id

Response example

{
  "data": {
    "id": "uuid",
    "name": "April 30",
    "kind": "individual",
    "participant_count": 42,
    "my_enrollment": null
  },
  "error": null
}
POST/api/v1/challenges/{id}/enrollBearer or API key

Enroll in a challenge

Enrolls the caller in the challenge. Optionally joins a team.

Parameters, scopes and examples

Path parameters

idstring · required
Challenge id

Optional team_id to join.

Request body

{
  "team_id": "uuid"
}

Response example

{
  "data": {
    "id": "uuid",
    "current_count": 0
  },
  "error": null
}
POST/api/v1/challenges/{id}/photosBearer or API key

Submit a challenge photo

Uploads (by URL) a photo to the caller’s enrollment. For transformation challenges set is_before or is_after.

Parameters, scopes and examples

Path parameters

idstring · required
Challenge id

Photo URL, optional caption, and marker flags.

Request body

{
  "enrollment_id": "uuid",
  "url": "https://...",
  "is_before": false,
  "is_after": false
}

Response example

{
  "data": {
    "id": "uuid",
    "url": "https://..."
  },
  "error": null
}
Clubs9 documented operations
GET/api/v1/clubsBearer or API key

List clubs

Returns clubs in the current venue. Visible clubs only (public + members_only when authed as member).

Parameters, scopes and examples

Response example

{
  "data": [
    {
      "id": "uuid",
      "slug": "morning-runners",
      "name": "Morning Runners"
    }
  ],
  "error": null
}
GET/api/v1/clubs/{id}Bearer or API key

Club detail

Returns a single visible club with member count and the caller’s membership status. Accepts a club id or slug.

Parameters, scopes and examples

Path parameters

idstring · required
Club id or slug

Response example

{
  "data": {
    "id": "uuid",
    "slug": "morning-runners",
    "name": "Morning Runners",
    "member_count": 18,
    "my_membership": null
  },
  "error": null
}
POST/api/v1/clubs/{id}/joinBearer or API key

Join a club

Joins a club. Subject to visibility + auto-approve rules.

Parameters, scopes and examples

Path parameters

idstring · required
Club id or slug

Response example

{
  "data": {
    "club_id": "uuid",
    "status": "active"
  },
  "error": null
}
POST/api/v1/clubs/{id}/leaveBearer or API key

Leave a club

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.

Parameters, scopes and examples

Query parameters

statusstring
pending | approved | rejected | changes_requested

Response example

{
  "data": [
    {
      "id": "uuid",
      "name": "Early Birds",
      "status": "pending"
    }
  ],
  "error": null
}
POST/api/v1/clubs/suggestionsBearer or API key

Suggest a new club

Members can suggest leading a new club when the venue has opted into suggestions.

Parameters, scopes and examples

Club details + why the suggester wants to lead.

Request body

{
  "name": "Early Birds",
  "slug": "early-birds",
  "category": "running",
  "description": "Morning runners group",
  "why_lead": "I run every morning and want company"
}

Response example

{
  "data": {
    "id": "uuid",
    "status": "pending"
  },
  "error": null
}
POST/api/v1/clubs/suggestions/{id}/approveBearer or API key

Approve a club suggestion (admin)

Approves a suggestion, creates the club, auto-assigns suggester as leader, posts to feed.

Parameters, scopes and examples

Path parameters

idstring · required
Suggestion id

Optional reviewer notes.

Request body

{
  "notes": "Looks great — approved."
}

Response example

{
  "data": {
    "club_id": "uuid"
  },
  "error": null
}
POST/api/v1/clubs/suggestions/{id}/rejectBearer or API key

Reject a club suggestion (admin)

Rejects the suggestion and notifies the suggester with the reason.

Parameters, scopes and examples

Path parameters

idstring · required
Suggestion id

Rejection reason.

Request body

{
  "reason": "Too niche for our community right now."
}

Response example

{
  "data": {
    "status": "rejected"
  },
  "error": null
}
Chat1 documented operation
GET/api/v1/chat/channels/{id}Bearer or API key

Chat channel detail

Returns a single chat channel summary (channel row + unread_count + last_message_at) for the authenticated org. Staff role (or chat.read scope) required.

Parameters, scopes and examples

Required scopes

chat.read

Path parameters

idstring · required
Channel id

Response example

{
  "data": {
    "id": "uuid",
    "name": "#general",
    "channel_type": "public",
    "unread_count": 3,
    "last_message_at": "iso"
  },
  "error": null
}
Barcodes7 documented operations
GET/api/v1/barcodes/lookupBearer or API key

Look up a barcode at POS

Resolve a scanned barcode to a mapping (pass, product, gift card). Exact match first, then longest prefix match.

Parameters, scopes and examples

Required scopes

pos.sell

Query parameters

codestring · required
The scanned barcode string

Response example

{
  "data": {
    "mapping": {
      "id": "uuid",
      "barcode": "7340999000001",
      "target_type": "pass_type",
      "target_id": "uuid",
      "target_label": "HYC Monthly Unlimited",
      "is_active": true
    },
    "match_type": "exact"
  },
  "error": null
}
GET/api/v1/barcodes/mappingsBearer or API key

List barcode mappings

List all barcode mappings for the venue.

Parameters, scopes and examples

Required scopes

pos.manage_barcodes

Query parameters

target_typestring
Filter by target type
batch_namestring
Filter by batch name
active_onlyboolean
Only active mappings
limitinteger
Max rows (≤500)Default: 100

Response example

{
  "data": {
    "data": [],
    "total": 0
  },
  "error": null
}
POST/api/v1/barcodes/mappingsBearer or API key

Create a barcode mapping

Map a barcode to a pass type, product, or gift card. Supports Idempotency-Key.

Parameters, scopes and examples

Required scopes

pos.manage_barcodes

Mapping details

Request body

{
  "barcode": "7340999000001",
  "match_type": "exact",
  "target_type": "pass_type",
  "target_id": "uuid",
  "target_label": "HYC Monthly Unlimited"
}

Response example

{
  "data": {
    "id": "uuid"
  },
  "error": null
}
PATCH/api/v1/barcodes/mappings/{id}Bearer or API key

Update a barcode mapping

Update label, target, activation state, or batch for an existing mapping.

Parameters, scopes and examples

Required scopes

pos.manage_barcodes

Path parameters

idstring · required
Mapping id

Fields to update

Request body

{
  "target_label": "Updated label",
  "is_active": false
}

Response example

{
  "data": {
    "id": "uuid"
  },
  "error": null
}
DELETE/api/v1/barcodes/mappings/{id}Bearer or API key

Deactivate a barcode mapping

Soft-delete a mapping so it no longer resolves. History is preserved.

Parameters, scopes and examples

Required scopes

pos.manage_barcodes

Path parameters

idstring · required
Mapping id

Response example

{
  "data": {
    "deactivated": true
  },
  "error": null
}
POST/api/v1/barcodes/bulk-scanBearer or API key

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.

Parameters, scopes and examples

Required scopes

pos.manage_barcodes

One scan at a time

Request body

{
  "barcode": "7340999000042",
  "target_type": "pass_type",
  "target_id": "uuid",
  "target_label": "HYC Monthly Unlimited",
  "batch_name": "Blue cards — Summer 2026",
  "allow_overwrite": false
}

Response example

{
  "data": {
    "status": "created",
    "mapping": {
      "id": "uuid",
      "barcode": "7340999000042"
    }
  },
  "error": null
}
POST/api/v1/barcodes/importBearer or API key

Bulk import barcode mappings

Import up to 5000 mappings at once. Duplicates are skipped, per-row errors returned.

Parameters, scopes and examples

Required scopes

pos.manage_barcodes

Array of rows

Request body

{
  "rows": [
    {
      "barcode": "7340999000001",
      "target_type": "pass_type",
      "target_id": "uuid",
      "target_label": "HYC Monthly Unlimited"
    }
  ]
}

Response example

{
  "data": {
    "imported": 847,
    "skipped": 12,
    "errors": []
  },
  "error": null
}
Room Rentals7 documented operations
GET/api/v1/roomsBearer or API key

List rental-enabled rooms

Returns every room with rental_enabled=true and is_active=true for the resolved org. Same data as the public /spaces page.

Parameters, scopes and examples

Query parameters

organization_iduuid
Override the caller's default org.

Response example

{
  "data": {
    "count": 2,
    "rooms": []
  },
  "error": null
}
GET/api/v1/rooms/[slug]Bearer or API key

Room rental detail

Full room detail (gallery, amenities, rate table, terms). Same data as /spaces/[slug].

Parameters, scopes and examples

Path parameters

slugstring · required
Room slug.

Response example

{
  "data": {
    "id": "uuid",
    "name": "Studio A",
    "hourly_rate": 250
  },
  "error": null
}
GET/api/v1/rooms/[slug]/availabilityBearer or API key

Room availability slots

30-min availability grid for the requested date. Busy slots are coarsely flagged — blocking_source is redacted so PII never leaks.

Parameters, scopes and examples

Query parameters

datestring · required
YYYY-MM-DD

Response example

{
  "data": {
    "count": 24,
    "date": "2026-05-01",
    "slots": []
  },
  "error": null
}
GET/api/v1/room-rentalsAPI key

List rentals

List room rentals for the API key's org. Filter by status / room_id / renter_id / start_after / start_before.

Parameters, scopes and examples

Required scopes

read:room_rentals

Response example

{
  "data": {
    "count": 12,
    "rentals": []
  },
  "error": null
}
POST/api/v1/room-rentalsAPI key

Create rental

Create a room rental. Conflicts checked against every event source. Idempotency-Key honored.

Parameters, scopes and examples

Required scopes

write:room_rentals

Rental input

Request body

{
  "room_id": "uuid",
  "renter_id": "uuid",
  "start_time": "2026-05-01T10:00:00Z",
  "end_time": "2026-05-01T12:00:00Z",
  "pricing_model": "hourly"
}

Response example

{
  "data": {
    "id": "uuid",
    "total_amount": 500,
    "status": "pending"
  },
  "error": null
}
GET/api/v1/room-rentals/[id]API key

Rental detail

Full rental detail.

Parameters, scopes and examples

Required scopes

read:room_rentals

Path parameters

iduuid · required
Rental id.

Response example

{
  "data": {
    "id": "uuid",
    "status": "confirmed"
  },
  "error": null
}
DELETE/api/v1/room-rentals/[id]API key

Cancel rental

Cancel a rental. Optional ?reason= is recorded; ?waive_fee=1 skips the cancellation fee.

Parameters, scopes and examples

Required scopes

write:room_rentals

Path parameters

iduuid · required
Rental id.

Query parameters

reasonstring
Cancellation reason (free text).
waive_feestring
Set to "1" to skip the cancellation fee.

Response example

{
  "data": {
    "id": "uuid",
    "status": "cancelled",
    "cancellation_fee_amount": 250
  },
  "error": null
}
MCP2 documented operations
POST/api/mcpAPI key

MCP JSON-RPC endpoint

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.

Parameters, scopes and examples

Required scopes

read:scheduleread:membersread:bookingsread:passesread:reportsread:operations

JSON-RPC 2.0 request (or array for batching).

Request body

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list"
}

Response example

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "search_schedule",
        "description": "…",
        "inputSchema": {
          "type": "object"
        }
      }
    ]
  }
}
GET/api/mcpPublic

MCP manifest

Tiny manifest so curl + browser tabs can sanity-check the endpoint exists. Real traffic is POST.

Parameters, scopes and examples

Response example

{
  "ok": true,
  "name": "booking-bible",
  "transport": "http",
  "protocol": "2025-03-26",
  "docs": "/docs/mcp"
}
Feature Toggles2 documented operations
GET/api/v1/admin/featuresBearer or API key

List feature modules + effective state

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.

Request body

{
  "enabled": true,
  "settings": {
    "window_days": 7
  }
}

Response example

{
  "data": {
    "ok": true
  },
  "error": null
}
Network14 documented operations
GET/api/v1/network/discoverBearer token

Discover network venues

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.

Parameters, scopes and examples

Query parameters

searchstring
Filter venues by name, slug, or city
limitinteger
Max venues to returnDefault: 50

Response example

{
  "data": [
    {
      "id": "uuid",
      "name": "Northside Fitness Vesterbro",
      "slug": "northside-fitness-vesterbro",
      "city": "København",
      "country_code": "DK",
      "class_type_count": 12,
      "member_count": 140,
      "is_partnered": true,
      "partnership_status": "active",
      "active_partnership_id": "uuid",
      "is_bookable": true
    }
  ],
  "error": null
}
GET/api/v1/network/creditsBearer token

Network credits

The caller’s network visit credits for the current calendar month — how many partner-venue visits remain under the partnership terms. Member-JWT.

Parameters, scopes and examples

Response example

{
  "data": [
    {
      "user_id": "uuid",
      "partnership_id": "uuid",
      "period_start": "2026-06-01",
      "visits_used": 1,
      "max_visits": 4
    }
  ],
  "error": null
}
POST/api/v1/network/bookBearer token

Book a network class

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.

Parameters, scopes and examples

Network booking

Request body

{
  "class_instance_id": "uuid",
  "pass_id": "uuid",
  "partnership_id": "uuid"
}

Response example

{
  "data": {
    "booking_id": "uuid",
    "status": "confirmed",
    "network_visit_id": "uuid"
  },
  "error": null
}
GET/api/v1/network/partnershipsBearer or API key

List venue Network partnerships

Lists venue-to-venue Network partnerships for the authenticated venue. Bearer JWT requires network.view; API keys require read:network.

Parameters, scopes and examples

Required scopes

read:network

Response example

{
  "data": [
    {
      "id": "uuid",
      "status": "active",
      "partnership_kind": "network"
    }
  ],
  "error": null
}
POST/api/v1/network/partnershipsBearer or API key

Create a venue Network partnership request

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.

Parameters, scopes and examples

Required scopes

write:network

Network partnership request

Request body

{
  "receiving_org_id": "uuid",
  "model": "pay_per_visit",
  "terms": {},
  "binding_months": 3
}

Response example

{
  "data": {
    "id": "uuid",
    "status": "pending",
    "partnership_kind": "network"
  },
  "error": null
}
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.

Parameters, scopes and examples

Required scopes

read:network

Path parameters

iduuid · required
Network partnership id

Response example

{
  "data": {
    "id": "uuid",
    "status": "active",
    "partnership_kind": "network"
  },
  "error": null
}
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.

Parameters, scopes and examples

Required scopes

write:network

Path parameters

iduuid · required
Network partnership id

Network partnership action or terms update

Request body

{
  "action": "terminated",
  "reason": "Partnership ended"
}

Response example

{
  "data": {
    "id": "uuid",
    "status": "terminated",
    "partnership_kind": "network"
  },
  "error": null
}
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.

Parameters, scopes and examples

Required scopes

write:network

Path parameters

iduuid · required
Network partnership id

Network termination reason

Request body

{
  "reason": "Partnership ended"
}

Response example

{
  "data": {
    "id": "uuid",
    "scheduled_termination_date": "2026-09-26T00:00:00.000Z"
  },
  "error": null
}
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.

Parameters, scopes and examples

Required scopes

read:network

Path parameters

iduuid · required
Network partnership id

Query parameters

fromstring
Lower created_at bound
tostring
Upper created_at bound
limitinteger
Max rows to returnDefault: 50

Response example

{
  "data": [
    {
      "id": "uuid",
      "partnership_id": "uuid",
      "status": "completed"
    }
  ],
  "error": null
}
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.

Parameters, scopes and examples

Required scopes

read:network

Path parameters

iduuid · required
Network partnership id

Query parameters

limitinteger
Max rows to returnDefault: 50

Response example

{
  "data": [
    {
      "id": "uuid",
      "settlement_kind": "network",
      "partnership_id": "uuid"
    }
  ],
  "error": null
}
GET/api/v1/admin/network/professionalsBearer token

List or look up professional collaborations

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.

Parameters, scopes and examples

Query parameters

emailstring
Exact professional email lookup for an invitation

Response example

{
  "data": [
    {
      "id": "uuid",
      "direction": "venue",
      "status": "active",
      "venue_org_id": "uuid",
      "venue_name": "Northside Fitness",
      "practitioner_org_id": "uuid",
      "practitioner_org_name": "Alex Professional",
      "professional_name": "Alex Morgan",
      "membership_role": "instructor",
      "comp_model": "per_class_flat",
      "rate_per_class": 500
    }
  ],
  "error": null
}
POST/api/v1/admin/network/professionalsBearer token

Invite a professional

Creates a pending venue-to-professional collaboration with an explicit venue role and compensation model. Requires network.manage and a non-individual venue workspace.

Parameters, scopes and examples

Professional collaboration invitation

Request body

{
  "practitioner_org_id": "uuid",
  "practitioner_user_id": "uuid",
  "membership_role": "instructor",
  "comp_model": "per_class_flat",
  "rate_per_class": 500
}

Response example

{
  "data": {
    "partnership_id": "uuid"
  },
  "error": null
}
PATCH/api/v1/admin/network/professionals/{id}Bearer token

Update a professional collaboration

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.

Parameters, scopes and examples

One collaboration lifecycle action

Request body

{
  "action": "set_role",
  "membership_role": "manager"
}

Response example

{
  "data": {
    "updated": true
  },
  "error": null
}
DELETE/api/v1/admin/network/professionals/{id}Bearer token

Terminate a professional collaboration

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

Request body

{
  "reason": "Engagement ended",
  "keep_membership": false
}

Response example

{
  "data": {
    "membership_deactivated": true
  },
  "error": null
}
Relationships3 documented operations
GET/api/v1/me/relationshipsBearer token

List my relationships

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.

Parameters, scopes and examples

Response example

{
  "data": [
    {
      "id": "uuid",
      "user_id": "uuid",
      "related_user_id": "uuid",
      "related_name": "Sam Doe",
      "related_email": "sam@example.com",
      "relationship_type": "family_member",
      "status": "active",
      "related_profile": {
        "first_name": "Sam",
        "last_name": "Doe",
        "email": "sam@example.com",
        "phone": null
      }
    }
  ],
  "error": null
}
POST/api/v1/me/relationshipsBearer token

Add a relationship

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.

Parameters, scopes and examples

Relationship

Request body

{
  "related_email": "sam@example.com",
  "related_name": "Sam Doe",
  "relationship_type": "family_member",
  "company_name": null
}

Response example

{
  "data": {
    "id": "uuid"
  },
  "error": null
}
DELETE/api/v1/me/relationshipsBearer token

Revoke a relationship

Revoke a relationship the caller is a party to (and its mirror row). Member-JWT.

Parameters, scopes and examples

Query parameters

relationship_idstring · required
Relationship id to revoke

Response example

{
  "data": {
    "id": "uuid",
    "status": "revoked"
  },
  "error": null
}
Private Events9 documented operations
GET/api/v1/venues/{slug}/event-typesPublic

List private-event types

Public catalog of bookable private-event types for a venue (active + shown on website). Used by the app inquiry screen. Cached, IP-throttled.

Parameters, scopes and examples

Path parameters

slugstring · required
Venue slug

Response example

{
  "data": [
    {
      "id": "uuid",
      "name": "Private Group Yoga",
      "slug": "private-group-yoga",
      "tagline": "Book the studio for your team",
      "category": "corporate",
      "min_participants": 5,
      "max_participants": 30,
      "default_duration_minutes": 90,
      "pricing_model": "per_person",
      "base_price": 0,
      "per_person_price": 250,
      "currency": "DKK",
      "deposit_required": true,
      "deposit_amount": 1000
    }
  ],
  "error": null
}
GET/api/v1/venues/{slug}/event-types/{eventSlug}Public

Private-event type detail

Public detail for a single private-event type (active + shown on website). 404 for hidden/draft types. Cached, IP-throttled.

Parameters, scopes and examples

Path parameters

slugstring · required
Venue slug
eventSlugstring · required
Event-type slug

Response example

{
  "data": {
    "id": "uuid",
    "name": "Private Group Yoga",
    "slug": "private-group-yoga"
  },
  "error": null
}
POST/api/v1/private-events/inquireBearer token

Create a private-event inquiry

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.

Parameters, scopes and examples

Inquiry

Request body

{
  "event_type_id": "uuid",
  "participant_count": 12,
  "start_time": "2026-07-01T17:00:00.000Z",
  "occasion": "Team offsite",
  "special_requests": "Mats for 12",
  "selected_addons": [
    {
      "name": "Smoothies",
      "price": 60,
      "qty": 12
    }
  ]
}

Response example

{
  "data": {
    "id": "uuid"
  },
  "error": null
}
GET/api/v1/private-eventsBearer token

List my private-event bookings

List the member’s private-event bookings (scoped by `contact_email` == the caller’s email). Member-JWT.

Parameters, scopes and examples

Response example

{
  "data": [
    {
      "id": "uuid",
      "status": "inquiry"
    }
  ],
  "error": null
}
GET/api/v1/private-events/{id}Bearer token

Private-event booking detail

Detail of one of the member’s private-event bookings. Member-JWT.

Parameters, scopes and examples

Path parameters

idstring · required
Booking id

Response example

{
  "data": {
    "id": "uuid",
    "status": "inquiry"
  },
  "error": null
}
DELETE/api/v1/private-events/{id}Bearer token

Cancel a private-event booking

Cancel one of the member’s private-event bookings. Member-JWT.

Parameters, scopes and examples

Path parameters

idstring · required
Booking id

Response example

{
  "data": {
    "id": "uuid",
    "status": "cancelled"
  },
  "error": null
}
POST/api/v1/private-events/{id}/payBearer token

Get a deposit/full payment intent for a booking

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.

Parameters, scopes and examples

Path parameters

idstring · required
Booking id

Response example

{
  "data": {
    "client_secret": "pi_..._secret_...",
    "amount": 250,
    "currency": "DKK",
    "payment_kind": "deposit",
    "customer_id": "cus_...",
    "ephemeral_key": "ek_...",
    "stripe_account_id": "acct_... (direct mode; otherwise null)",
    "merchant_country_code": "DK",
    "regional_revision": "org:venue-id:r4"
  },
  "error": null
}
POST/api/v1/private-events/{id}/approveBearer token

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).

Parameters, scopes and examples

Path parameters

idstring · required
Booking id

Response example

{
  "data": {
    "status": "confirmed",
    "client_secret": "pi_..._secret_...",
    "payment_intent_id": "pi_...",
    "customer_id": "cus_...",
    "ephemeral_key": "ek_...",
    "stripe_account_id": "acct_... (direct mode; otherwise null)",
    "merchant_country_code": "DK",
    "regional_revision": "org:venue-id:r4",
    "amount": 250,
    "currency": "DKK",
    "payment_kind": "deposit"
  },
  "error": null
}
GET/api/v1/widget/private-sessionsPublic

Private-sessions widget catalog

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.

Parameters, scopes and examples

Query parameters

venuestring · required
Venue slug

Response example

{
  "data": {
    "venue": {
      "slug": "harbor-movement",
      "name": "Harbor Movement"
    },
    "event_types": [
      {
        "id": "uuid",
        "name": "Private Group Session",
        "slug": "private-group-session",
        "pricing_model": "per_person",
        "per_person_price": 250,
        "currency": "DKK",
        "payment_mode": "deposit"
      }
    ]
  },
  "error": null
}
Products7 documented operations
GET/api/v1/admin/productsBearer or API key

List venue products

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.

Parameters, scopes and examples

Path parameters

slugstring · required
Venue slug

Response example

{
  "data": [
    {
      "id": "uuid",
      "product_id": "uuid",
      "name": "10-pack mat rental",
      "description": "Save 15%",
      "quantity": 10,
      "price": 450,
      "currency": "DKK",
      "savings_label": "Save 15%",
      "validity_days": 180
    }
  ],
  "error": null
}
POST/api/v1/product-packages/{id}/purchaseBearer token

Buy a clip card

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.

Parameters, scopes and examples

Path parameters

idstring · required
Product package id

Response example

{
  "data": {
    "client_secret": "pi_xxx_secret_xxx",
    "customer_id": "cus_xxx",
    "ephemeral_key": "ek_test_xxx",
    "stripe_account_id": null,
    "merchant_country_code": "DK",
    "regional_revision": "org:venue-id:r4",
    "payment_intent_id": "pi_xxx",
    "package_id": "uuid",
    "amount": 450,
    "currency": "DKK"
  },
  "error": null
}
Community1 documented operation
POST/api/v1/events/{id}/ticketsBearer token

Buy community-event tickets

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.

Parameters, scopes and examples

Path parameters

idstring · required
Event booking id

Ticket order

Request body

{
  "ticket_count": 2,
  "guest_names": [
    "Sam Doe"
  ],
  "notes": "Front row please"
}

Response example

{
  "data": {
    "client_secret": "pi_xxx_secret_xxx",
    "customer_id": "cus_xxx",
    "ephemeral_key": "ek_test_xxx",
    "stripe_account_id": null,
    "merchant_country_code": "DK",
    "regional_revision": "org:venue-id:r4",
    "payment_intent_id": "pi_xxx",
    "ticket_id": "uuid",
    "amount": 300,
    "currency": "DKK"
  },
  "error": null
}

Prefer the machine-readable contract?

The OpenAPI 3.1 document is generated from this same registry.

Open OpenAPI JSON

Partner engineering

Bring us the integration you want to build

For partner access, implementation questions or a contract review, contact integration@bookingbible.com.