PraxBook.
Developers

API reference

Build on PraxBook — read your services and availability, list bookings, and react to events in real time. A small, predictable REST API over HTTPS that returns JSON.

Pro API access is a Pro feature. Create and manage keys in the console under Settings → Embed & API.
↓ Download OpenAPI spec

Machine-readable OpenAPI 3.0 — generate a typed client SDK from it (e.g. with openapi-generator).

Authentication

Every request to /api/ext/v1 is authenticated with an API key sent as a Bearer token in the Authorization header.

Mint an API key in the console under Settings → Embed & API (Pro plan). The raw key is shown exactly once at creation — store it securely, as it can't be recovered later. Send it on every /api/ext/v1 request as 'Authorization: Bearer <api_key>'. Each key is scoped to a single salon account and only ever sees that account's own data.

Base URL
https://booking.thenextbeacon.com
Header
Authorization: Bearer <api_key>

Example request

curl https://booking.thenextbeacon.com/api/ext/v1/services \
  -H "Authorization: Bearer <api_key>"

Rate limits

The API is intended for back-office integrations, not high-volume traffic. Keep to a few requests per second per key; sustained bursts may be throttled (HTTP 429). Booking creation is additionally guarded against double-booking the same staff member.

Bookings

Read, create and cancel appointments for your salon. Times in responses are UTC, marked with a trailing 'Z'. Times you SEND (create/reschedule start) may carry an explicit offset or 'Z'; a bare string like '2026-06-01T10:00:00' is read as your salon's local wall-clock time.

GET/api/ext/v1/bookings

List your bookings, scoped to your team, ordered by id. Cursor-paginated: pass starting_after with the previous page's nextCursor to fetch the next page; stop when hasMore is false.

Query parameters
limitMax rows to return (default 50, max 200).
starting_afterBooking id cursor — return rows after this id. Use the nextCursor from the previous page.
Example response
{
  "data": [
    {
      "uid": "abc123def456ghi789jkl0",
      "id": 4821,
      "title": "Épilation Sourcils",
      "start": "2026-06-01T08:00:00Z",
      "end": "2026-06-01T08:30:00Z",
      "status": "accepted",
      "serviceSlug": "epilation-sourcils",
      "amountChf": 30.0,
      "clientName": "Marie Dupont",
      "clientEmail": "[email protected]",
      "clientPhone": "+41 79 123 45 67"
    }
  ],
  "hasMore": true,
  "nextCursor": 4821
}
GET/api/ext/v1/bookings/{uid}

Fetch a single booking by its uid.

Example response
{
  "uid": "abc123def456ghi789jkl0",
  "id": 4821,
  "title": "Épilation Sourcils",
  "start": "2026-06-01T08:00:00Z",
  "end": "2026-06-01T08:30:00Z",
  "status": "accepted",
  "serviceSlug": "epilation-sourcils",
  "amountChf": 30.0,
  "clientName": "Marie Dupont",
  "clientEmail": "[email protected]",
  "clientPhone": "+41 79 123 45 67"
}
POST/api/ext/v1/bookings

Create a booking. Identify the service by serviceSlug OR eventTypeId. The end time is computed from durationMin, or the service's default length when omitted. staffId and note are optional.

Request body
serviceSlugepilation-sourcils
eventTypeId102
start2026-06-01T10:00:00
durationMin30
attendee{"name": "Marie Dupont", "email": "[email protected]", "phone": "+41 79 123 45 67"}
staffId7
noteFirst visit — slight latex allergy.
Example response
{
  "uid": "abc123def456ghi789jkl0",
  "id": 4821,
  "status": "accepted"
}
POST/api/ext/v1/bookings/{uid}/cancel

Cancel a booking by uid (sets its status to 'cancelled').

Example response
{
  "ok": true,
  "status": "cancelled"
}
POST/api/ext/v1/bookings/{uid}/reschedule

Move a booking to a new time. The end is recomputed from durationMin (or the service length); a staff clash returns 409.

Request body
start2026-06-02T11:45:00
durationMin30
Example response
{
  "ok": true,
  "uid": "abc123def456ghi789jkl0",
  "start": "2026-06-02T09:45:00Z",
  "end": "2026-06-02T10:15:00Z"
}

Services

Your bookable service menu and real-time availability.

GET/api/ext/v1/services

List your bookable services.

Example response
{
  "services": [
    {
      "slug": "epilation-sourcils",
      "title": "Épilation Sourcils",
      "durationMinutes": 30,
      "price": 30.0,
      "currency": "CHF"
    }
  ]
}
GET/api/ext/v1/services/{slug}

Fetch a single service's detail by slug, including its specific bookingFields.

Example response
{
  "slug": "epilation-sourcils",
  "title": "Épilation Sourcils",
  "durationMinutes": 30,
  "price": 30.0,
  "currency": "CHF",
  "description": "Mise en forme des sourcils à la cire.",
  "bookingFields": [
    {
      "name": "notes",
      "type": "textarea",
      "required": false,
      "label": {
        "en": "Notes",
        "fr": "Notes / Précisions"
      }
    }
  ]
}
PATCH/api/ext/v1/services/{slug}

Update a service's price, duration and/or title. Send only the fields you want to change (at least one is required). price is in your salon's currency; duration is in minutes.

Request body
price35.0
duration45
titleÉpilation Sourcils
Example response
{
  "ok": true,
  "slug": "epilation-sourcils",
  "price": 35.0,
  "duration": 45,
  "title": "Épilation Sourcils"
}
GET/api/ext/v1/availability

Available start times for a service on a given day (ISO 8601 datetimes).

Query parameters
serviceSlugSlug of the service to check.
dateDay to check, formatted YYYY-MM-DD.
Example response
{
  "serviceSlug": "epilation-sourcils",
  "date": "2026-06-01",
  "slots": [
    "2026-06-01T10:00:00.000+02:00",
    "2026-06-01T10:30:00.000+02:00"
  ]
}

Forms

Multi-language configurations for Global Questions and Service-Specific Forms.

GET/api/ext/v1/forms/global

Fetch the global booking questions (standard fields) with localization.

Example response
{
  "fields": [
    {
      "name": "name",
      "type": "name",
      "required": true,
      "label": {
        "en": "Full name",
        "fr": "Nom complet"
      }
    },
    {
      "name": "referralSource",
      "type": "radio",
      "required": false,
      "label": {
        "en": "How did you hear about us?",
        "fr": "Comment avez-vous entendu parler de nous?"
      },
      "options": [
        {
          "label": {
            "en": "Instagram",
            "fr": "Instagram"
          },
          "value": "instagram"
        }
      ]
    }
  ]
}
GET/api/ext/v1/forms

List all active service-specific (intake) forms and their attachments.

Example response
{
  "forms": [
    {
      "id": 1,
      "name": "Consent Form",
      "description": "Lash lift consent",
      "fields": [
        {
          "name": "allergy",
          "type": "checkbox",
          "required": true,
          "label": {
            "en": "I have no allergies",
            "fr": "Je n'ai pas d'allergies"
          }
        }
      ],
      "links": [
        {
          "scope": "service",
          "ref": "rehaussement-cils"
        }
      ]
    }
  ]
}

Clients

Your CRM — client profiles and their visit history.

GET/api/ext/v1/clients

List your clients with visit counts and last-visit dates, ordered by id. Cursor-paginated: pass starting_after with the previous page's nextCursor to fetch the next page; stop when hasMore is false.

Query parameters
limitMax rows to return (default 50, max 200).
starting_afterClient id cursor — return rows after this id. Use the nextCursor from the previous page.
qFilter by name or email substring.
Example response
{
  "data": [
    {
      "email": "[email protected]",
      "name": "Marie Dupont",
      "phone": "+41 79 123 45 67",
      "totalVisits": 4,
      "lastVisit": "2026-05-12T14:30:00"
    }
  ],
  "hasMore": true,
  "nextCursor": 318
}
GET/api/ext/v1/clients/{email}

A client's profile plus their recent bookings (team-scoped).

Example response
{
  "email": "[email protected]",
  "name": "Marie Dupont",
  "phone": "+41 79 123 45 67",
  "notes": "Prefers afternoon appointments.",
  "birthDate": "1990-03-21",
  "totalVisits": 4,
  "bookings": [
    {
      "uid": "abc123def456ghi789jkl0",
      "id": 4821,
      "title": "Épilation Sourcils",
      "start": "2026-05-12T12:30:00Z",
      "end": "2026-05-12T13:00:00Z",
      "status": "accepted",
      "serviceSlug": "epilation-sourcils",
      "amountChf": 30.0,
      "clientName": "Marie Dupont",
      "clientEmail": "[email protected]",
      "clientPhone": "+41 79 123 45 67"
    }
  ]
}

Catalog

Public-facing salon profile, reviews, deals, memberships and coupon checks.

GET/api/ext/v1/business

Your salon's business profile (address, contact, hours, socials).

Example response
{
  "name": "My Salon",
  "address": "Rue du Commerce 12, 1204 Genève",
  "street": "Rue du Commerce 12",
  "city": "Genève",
  "postalCode": "1204",
  "country": "CH",
  "phone": "+41 22 123 45 67",
  "email": "[email protected]",
  "website": "https://example.com",
  "hours": [
    {
      "days": [
        1,
        2,
        3,
        4,
        5
      ],
      "opens": "09:00",
      "closes": "18:00"
    }
  ],
  "currency": "CHF",
  "latitude": 46.2044,
  "longitude": 6.1432,
  "priceRange": "$$",
  "paymentAccepted": "Cash, Visa, Mastercard, TWINT",
  "socials": {
    "instagram": "https://instagram.com/mysalon",
    "tiktok": "",
    "googleMaps": "https://maps.google.com/?cid=123",
    "directions": "https://maps.google.com/dir/?api=1&destination=..."
  }
}
GET/api/ext/v1/reviews

Your approved customer reviews plus the aggregate rating.

Query parameters
limitMax reviews to return (default 50, max 200).
offsetReviews to skip for pagination (default 0).
Example response
{
  "ratingValue": 4.8,
  "ratingCount": 126,
  "reviews": [
    {
      "id": 91,
      "authorName": "Marie D.",
      "rating": 5,
      "body": "Service impeccable, je reviendrai !",
      "verified": true,
      "reply": "Merci Marie, à bientôt !",
      "createdAt": "2026-05-10T16:22:00"
    }
  ]
}
GET/api/ext/v1/deals

Your currently-live last-minute deals.

Example response
{
  "deals": [
    {
      "title": "Mardi tranquille -20%",
      "discountPct": 20,
      "scope": "all",
      "serviceSlug": null,
      "couponCode": "DEAL-K7M2PQ",
      "endsAt": "2026-06-03T18:00:00+02:00"
    }
  ]
}
GET/api/ext/v1/memberships

Your active membership plans.

Example response
{
  "memberships": [
    {
      "id": 3,
      "name": "VIP mensuel",
      "priceChf": 49.0,
      "interval": "monthly",
      "includedCredits": 1,
      "discountPct": 10.0,
      "perks": "1 soin offert + 10% sur tout le reste."
    }
  ]
}
GET/api/ext/v1/waitlist

Your active waitlist entries (clients waiting for a spot).

Example response
{
  "entries": [
    {
      "id": 12,
      "name": "Marie D.",
      "email": "[email protected]",
      "phone": "",
      "service": "pose-classique",
      "notes": "",
      "language": "fr",
      "createdAt": "2026-05-20T10:00:00"
    }
  ]
}
POST/api/ext/v1/waitlist

Add a client to the waitlist; they're alerted automatically in their language when a matching slot frees up.

Request body
nameMarie Dupont
email[email protected]
servicepose-classique
localefr
Example response
{
  "ok": true,
  "id": 13
}
GET/api/ext/v1/templates

List your message templates (event, channel, locales).

Example response
{
  "templates": [
    {
      "id": 4,
      "name": "Booking cancelled",
      "event": "booking_cancellation",
      "channel": "email",
      "active": true,
      "isDefault": true,
      "locales": [
        "fr",
        "en"
      ]
    }
  ]
}
POST/api/ext/v1/emails/send

Send a styled (branded HTML) email to a customer — via one of your templates ('event') or a custom subject/body wrapped in your salon's branded shell. Sent on demand (transactional).

Request body
to[email protected]
subjectA little reminder
body["Your next visit is coming up.", "See you soon!"]
ctaLabelBook again
ctaUrlhttps://booking.thenextbeacon.com/book/your-salon
localefr
Example response
{
  "ok": true,
  "delivered": true
}
POST/api/ext/v1/reviews/request

Send a styled review-request email (uses your configured review URL + copy). Trigger it from your POS/CRM after a visit. delivered=false if no review URL is configured.

Request body
to[email protected]
nameMarie
localefr
Example response
{
  "ok": true,
  "delivered": true
}
POST/api/ext/v1/coupons/validate

Check whether a coupon code is valid for a service and base price, and what it would discount. This is a read — it does NOT redeem the code.

Request body
codeDEAL-K7M2PQ
serviceSlugepilation-sourcils
baseChf30.0
Example response
{
  "valid": true,
  "discountChf": 6.0,
  "reason": null
}

Loyalty

Read and adjust a client's loyalty point balance.

GET/api/ext/v1/loyalty/{email}

Fetch a client's current loyalty point balance and their last 20 ledger entries.

Example response
{
  "email": "[email protected]",
  "balance": 340,
  "history": [
    {
      "points": 50,
      "reason": "booking #4821",
      "createdAt": "2026-05-12T14:30:00"
    },
    {
      "points": -100,
      "reason": "redeemed",
      "createdAt": "2026-04-20T11:00:00"
    }
  ]
}
POST/api/ext/v1/loyalty/{email}/adjust

Manually add or remove points for a client. Pass a negative points value to deduct. Returns the new balance after the adjustment.

Request body
points50
reasonIn-store gift
Example response
{
  "ok": true,
  "newBalance": 390
}

Packages

Read package purchase records.

GET/api/ext/v1/packages/purchases

List paid package purchases, newest first (max 100). Filter by buyer email with the optional email query param.

Query parameters
emailFilter to purchases by this buyer email address.
Example response
{
  "purchases": [
    {
      "id": 17,
      "email": "[email protected]",
      "name": "Marie Dupont",
      "package": "10-Visit Pack",
      "kind": "credits",
      "code": "PKG-ABC123",
      "status": "paid",
      "createdAt": "2026-05-28T09:15:00"
    }
  ]
}

Webhooks

Subscribe to events; payloads are POSTed to your URL, signed with HMAC-SHA256 in the X-PraxBook-Signature header. Verify the signature against your webhook secret before trusting a payload.

Events

  • booking.created
  • booking.cancelled
  • review.created
  • client.created
  • package.purchased

Example payload

{
  "event": "booking.created",
  "booking": {
    "uid": "abc123def456ghi789jkl0",
    "start": "2026-06-01T08:00:00Z",
    "status": "accepted"
  }
}