API · Beta

Embed Alera into your site.

Booking, payments, waitlists, cancellations — your brand on the surface, Alera as the engine. Per-tenant API keys, REST endpoints, Stripe Connect under the hood.

Last updated May 23, 2026

Section 01

Quickstart

  1. Sign in at alerabooking.comand mint a key from Profile → Settings → API keys. Add your site's production origin (e.g. https://your-site.com) to the allowlist. The plaintext is shown once — copy it into your server-side env as ALERA_API_KEY.
  2. Connect your Stripe account on the same Profile screen if you haven't already. Bookings against paid classes mint a Stripe Checkout session against your connected account using Direct Charges — funds land in your bank, not Alera's.
  3. From your site's server, hit the endpoints below with x-alera-key on every request. Render the response how you want; Alera stays invisible to your customers.

Section 02

Authentication

Three authentication patterns, each scoped to a specific use case. Every endpoint card below flags which pattern it expects.

PatternHeaderUsed by
Tenant keyx-alera-key + matching OriginServer-side calls from your backend.
Signed tokenIn the request bodyPer-booking HMAC tokens — cancel links, post-visit tips.
Customer tokenx-customer-token (or ?token=)Magic-link customer portal session (30-day).
  • Send every API-key request with x-alera-key: alr_xxx_yyyy…. The header value is the full plaintext you copied at key creation.
  • Send a matching Originheader (browsers add it automatically; from a server, set it explicitly). The Origin must be in the key's allowedOrigins list or the call returns 403.
  • Keys are server-side credentials. Never embed in front-end JavaScript, never commit to source control. Treat them like Stripe secret keys.
  • To rotate: create a new key, swap your site env, then revoke the old one. Zero downtime. Revocation is instant.
  • Signed-token routes (cancel, tip) accept their token inside the request body or URL — no API key needed. Tokens are HMAC-signed by a per-purpose secret and bound to a specific record id, so they can't cross-replay.

Section 03

Endpoints

11 endpoints across 4 groups. Each card carries a request shape, a 200 response example, and the failure modes you'll need to handle. Code samples copy as-is — replace ALERA_API_KEY with your mint.

Classes

List and book group-class sessions. Reads are cacheable; writes mint a Stripe Checkout when the class has a price.

GET/api/classesTenant key

List upcoming scheduled class sessions for a provider. Filtered to status=Scheduled; cancelled and completed sessions never appear. The response always reflects bookable state.

Query

  • handle (required)The provider's handle (e.g. sculpt-co).
  • from (required)Inclusive start of the date range, YYYY-MM-DD.
  • to (required)Inclusive end of the date range, YYYY-MM-DD. Maximum 60 days from from.
curl 'https://alerabooking.com/api/classes?handle=sculpt-co&from=2026-05-13&to=2026-06-13' \
  -H 'x-alera-key: alr_sclp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
  -H 'Origin: https://your-site.com'

Response (200)

{
  "classes": [
    {
      "id": "class-7a2c",
      "scheduledAt": "2026-05-18T11:00:00.000Z",
      "nameSnapshot": "Pilates Class",
      "durationMin": 50,
      "priceCents": 2000,
      "capacity": 6,
      "enrolledCount": 4,
      "seatsLeft": 2,
      "publicNote": null,
      "instructor": { "id": "ins-...", "name": "Sarah Lee" }
    }
  ],
  "blackouts": [
    {
      "id": "bo-...",
      "label": "Studio closed for Memorial Day",
      "startsAt": "2026-05-25T00:00:00.000Z",
      "endsAt": "2026-05-27T00:00:00.000Z",
      "allDay": true,
      "kind": "holiday"
    }
  ]
}

Errors

  • 400 Missing or invalid query params
  • 401 Invalid or revoked API key
  • 403 Origin not allowed for this API key, or handle does not match the key's provider
  • 429 Rate-limit exceeded (60/min/IP)
GET/api/classes/:idTenant key

Single class-session detail plus a public-safe slice of the instructor (Provider). Useful for rendering a class-detail view without a second round-trip.

curl 'https://alerabooking.com/api/classes/class-7a2c' \
  -H 'x-alera-key: alr_sclp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
  -H 'Origin: https://your-site.com'

Response (200)

{
  "class": {
    "id": "class-7a2c",
    "scheduledAt": "2026-05-18T11:00:00.000Z",
    "nameSnapshot": "Pilates Class",
    "durationMin": 50,
    "priceCents": 2000,
    "capacity": 6,
    "enrolledCount": 4,
    "seatsLeft": 2,
    "publicNote": null
  },
  "provider": {
    "displayName": "Cynthia",
    "handle": "sculpt-co",
    "avatarKey": "providers/...",
    "bio": null,
    "timezone": "America/Chicago"
  }
}

Errors

  • 404 Class not found (or scoped to a different provider's key)
  • 410 Class was cancelled
POST/api/classes/:id/bookTenant key

Reserve a seat and (when the class has a price > 0) mint a Stripe Checkout Session. The customer is redirected to Stripe for payment; on completion the webhook stamps the booking as paid + increments enrolledCount. The response includes a stateless HMAC cancelToken bound to the enrollment — surface it as a self-serve cancel link in the confirmation page or email.

Body

  • name (string, required) — Customer's name.
  • email (string, required) — Customer's email — used for confirmation + Stripe receipt.
  • phone (string, required) — Phone (10+ digits). Used for reminders + dedupe across bookings.
  • successUrl (string, optional) — Where Stripe should redirect after payment. Must share the request's Origin.
  • cancelUrl (string, optional) — Where Stripe should redirect on payment abandon. Must share the request's Origin.
  • useClientPackageId (string, optional) — Phase 5 — redeem a class credit from a ClientPackage instead of charging the card. Requires customerToken.
  • customerToken (string, optional) — Magic-link customer session token (cust-v1). Pair with useClientPackageId to authenticate the redemption.
curl -X POST 'https://alerabooking.com/api/classes/class-7a2c/book' \
  -H 'x-alera-key: alr_sclp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
  -H 'Origin: https://your-site.com' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Jane Doe",
    "email": "jane@example.com",
    "phone": "5555550100",
    "successUrl": "https://your-site.com/book/confirm?session_id={CHECKOUT_SESSION_ID}",
    "cancelUrl": "https://your-site.com/book/cancelled"
  }'

Response (200)

{
  "enrollmentId": "enr-9d8f",
  "checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_...",
  "checkoutSessionId": "cs_test_...",
  "cancelToken": "djF8ZW5yL...",
  "classSession": {
    "id": "class-7a2c",
    "nameSnapshot": "Pilates Class",
    "scheduledAt": "2026-05-18T11:00:00.000Z",
    "durationMin": 50
  }
}

Errors

  • 400 Validation failed (bad email/phone, off-origin redirect URLs)
  • 401 customerToken missing or invalid when useClientPackageId is set
  • 403 Sign-in token doesn't match the class's provider
  • 409 Class is full (book → waitlist) — or the customer has already booked this class, or has no credits left on the pack
  • 410 Class was cancelled or has already started
  • 502 Stripe Checkout creation failed (provider may not have Connect onboarded)
POST/api/classes/:id/waitlistTenant keyIdempotent

Join the waitlist for a class whose enrolledCount has reached capacity. Idempotent: an existing un-cancelled, un-promoted entry for the same customer returns the existing row. Promotion is manual — the provider sees a seat-opened notification and chooses who to promote.

Body

  • name (string, required) — Customer's name.
  • email (string, required) — Customer's email.
  • phone (string, required) — Phone (10+ digits).
curl -X POST 'https://alerabooking.com/api/classes/class-7a2c/waitlist' \
  -H 'x-alera-key: alr_sclp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
  -H 'Origin: https://your-site.com' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Jane Doe","email":"jane@example.com","phone":"5555550100"}'

Response (200)

{
  "waitlistEntryId": "wl-3c1b",
  "position": 2,
  "alreadyOnWaitlist": false
}

Errors

  • 409 Seats are still available — book directly instead
  • 410 Class was cancelled or has already started

Bookings

Customer-facing cancellation flow, authenticated by the per-booking HMAC token (not the API key).

POST/api/enrollments/:id/cancelSigned tokenIdempotent

Customer-initiated cancellation, authenticated by the cancelToken returned at booking time. Full refund if scheduledAt − now ≥ service.cancelWindowHours (default 24h). Outside the window, the seat frees up but the payment is not refunded. The token's signed enrollment id must match the URL path id. Repeated calls on an already-cancelled booking return the same response.

Body

  • cancelToken (string, required) — The HMAC token returned by /book.
  • cancelReason (string, optional) — Optional customer-supplied reason (≤500 chars).
curl -X POST 'https://alerabooking.com/api/enrollments/enr-9d8f/cancel' \
  -H 'Content-Type: application/json' \
  -d '{"cancelToken":"djF8ZW5yL..."}'

Response (200)

{
  "ok": true,
  "cancelledAt": "2026-05-14T22:10:00.000Z",
  "lateCancel": false,
  "cancelWindowHours": 24,
  "refundedCents": 2000,
  "refundId": "re_xxx"
}

Errors

  • 400 Missing cancel token
  • 401 Token is malformed / bad-signature / expired / does not match the path id
  • 410 Booking is already Completed or NoShow
  • 502 Refund failed at Stripe; booking is intentionally NOT marked cancelled

Payments

Open-value gift cards, class packs, and post-visit tips. All routes mint Stripe Checkout sessions against the provider's connected account.

POST/api/packages/startTenant key

Begin a class-pack purchase. Returns a Stripe Checkout URL; on payment completion the webhook creates a ClientPackage row with the configured session count and emails the buyer a portal magic-link they can use to redeem credits at future bookings.

Body

  • packageTypeId (string, required) — The provider's PackageType id (configured by the studio owner).
  • handle (string, required) — Provider handle (must match the API key's scope).
  • name (string, required) — Buyer's name.
  • email (string, required) — Buyer's email.
  • phone (string, optional) — Optional phone for SMS receipts.
curl -X POST 'https://alerabooking.com/api/packages/start' \
  -H 'x-alera-key: alr_sclp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
  -H 'Origin: https://your-site.com' \
  -H 'Content-Type: application/json' \
  -d '{"packageTypeId":"pkg-...","handle":"sculpt-co","name":"Jane","email":"jane@example.com","phone":"5555550100"}'

Response (200)

{
  "ok": true,
  "checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_...",
  "checkoutSessionId": "cs_test_..."
}

Errors

  • 400 Validation failed (missing required field, package not available)
  • 403 API key not authorized for this provider
  • 412 Provider doesn't have Stripe Connect onboarded yet
  • 502 Stripe Checkout creation failed
POST/api/gift-cards/startTenant key

Mint a Stripe Checkout Session for an open-value gift card. Amount range is $10..$5,000. The recipient* fields are optional; if filled, the GiftCard email goes to the recipient instead of the purchaser. The GiftCard row + emailed code are created by the Stripe webhook on payment completion — no DB write at this step.

Body

  • handle (string, required) — Provider handle (must match the API key's scope).
  • amountCents (number, required) — Card value in cents. Min 1000 ($10), max 500000 ($5,000).
  • purchaserName (string, required) — Buyer's name. Appears on the gift card email + Stripe receipt.
  • purchaserEmail (string, required) — Buyer's email. Required for delivery + Stripe receipt.
  • recipientName (string, optional) — Recipient's name. If set with recipientEmail, the gift email goes to the recipient instead of the purchaser.
  • recipientEmail (string, optional) — Recipient's email.
  • giftMessage (string, optional) — Optional personal message (≤500 chars), included in the gift email.
curl -X POST 'https://alerabooking.com/api/gift-cards/start' \
  -H 'x-alera-key: alr_sclp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
  -H 'Origin: https://your-site.com' \
  -H 'Content-Type: application/json' \
  -d '{
    "handle": "sculpt-co",
    "amountCents": 10000,
    "purchaserName": "Jane Doe",
    "purchaserEmail": "jane@example.com",
    "recipientName": "Sam Smith",
    "recipientEmail": "sam@example.com",
    "giftMessage": "Happy birthday!"
  }'

Response (200)

{
  "ok": true,
  "checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_...",
  "checkoutSessionId": "cs_test_..."
}

Errors

  • 400 Validation failed (missing field, amount out of $10–$5,000 range)
  • 404 Provider not found for this handle
  • 412 Provider doesn't have Stripe Connect onboarded yet
  • 429 Rate-limit exceeded (20/min/IP)
  • 502 Stripe Checkout creation failed
POST/api/gift-cards/redeemTenant keyIdempotent

Validate and (optionally) debit a gift-card code against an upcoming appointment. Two modes: "preview" returns the redeemable amount without debiting; "commit" debits the card and appends to its redemption history. Commit is idempotent on appointmentId — a retry with the same appointmentId returns the prior debit with alreadyRedeemed=true. Codes are 16 chars, case-insensitive, alphanumerics only.

Body

  • code (string, required) — 16-character gift-card code (alphanumeric, case-insensitive).
  • providerId (string, required) — Provider id. Cards are per-pro; mismatch returns 404.
  • requestedDebitCents (number, required) — How much to debit. Capped server-side at the current balanceCents.
  • appointmentId (string, optional) — Required for commit mode to make the debit idempotent. Optional in preview mode.
  • mode (string, optional) — "preview" (default — no write) or "commit" (debit + append to history).
curl -X POST 'https://alerabooking.com/api/gift-cards/redeem' \
  -H 'x-alera-key: alr_sclp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
  -H 'Origin: https://your-site.com' \
  -H 'Content-Type: application/json' \
  -d '{
    "code": "ABC123XYZ4567890",
    "providerId": "prov-...",
    "requestedDebitCents": 2000,
    "mode": "preview"
  }'

Response (200)

{
  "ok": true,
  "debitCents": 2000,
  "balanceCents": 10000,
  "balanceRemainingCents": 8000
}

Errors

  • 400 Invalid code length, expired card, zero-balance card, or invalid amount
  • 404 Gift card not found or not valid for this provider
  • 429 Rate-limit exceeded (30/min/IP)
POST/api/tip/startSigned token

Post-visit tip Checkout Session. Authenticated by the tip-v1 HMAC token emailed to the customer (the URL fragment in their post-visit email). The token is purpose-namespaced — a leaked cancelToken can't be replayed here. Tip range is $1..$1,000. The webhook stamps tipChargeId + tipCapturedAt + tipAmountCents on the appointment after capture.

Body

  • token (string, required) — tip-v1 signed token from the post-visit email link.
  • amountCents (number, required) — Tip amount in cents. Min 100 ($1), max 100000 ($1,000).
curl -X POST 'https://alerabooking.com/api/tip/start' \
  -H 'Content-Type: application/json' \
  -d '{"token":"dGlwLXYxfC4uLg","amountCents":1000}'

Response (200)

{
  "checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_...",
  "checkoutSessionId": "cs_test_..."
}

Errors

  • 400 Amount out of $1–$1,000 range, tips disabled on the service, or appointment in a non-tippable state
  • 403 Tip token expired or signature-invalid
  • 404 Appointment not found
  • 409 A tip has already been collected for this appointment
  • 412 Provider isn't accepting tips through Stripe Connect
  • 429 Rate-limit exceeded (20/min/IP)
  • 502 Stripe Checkout creation failed

Customer

Magic-link sign-in + the authenticated customer summary (enrollments, packs, calendar feed).

POST/api/customer/signin-linkTenant key

Email a one-tap magic-link to a customer. The link opens /me/<token> on your site with a 30-day token. Anti-enumeration: unknown emails return 200 with no email sent.

Body

  • email (string, required) — Customer's email — matched against (providerId, email) in our DB.
  • handle (string, required) — Provider handle.
  • customerSiteOrigin (string, optional) — Override for the magic-link destination origin. Defaults to the request's Origin.
curl -X POST 'https://alerabooking.com/api/customer/signin-link' \
  -H 'x-alera-key: alr_sclp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
  -H 'Origin: https://your-site.com' \
  -H 'Content-Type: application/json' \
  -d '{"email":"jane@example.com","handle":"sculpt-co"}'

Response (200)

{ "ok": true }

Errors

  • 400 Missing email or handle
  • 403 API key not authorized for this provider
  • 429 Rate-limit exceeded (5/min/IP)
GET/api/customer/meCustomer token

Fetch the customer's enrollments + active package credits + calendar-subscription URL. Token is the cust-v1 HMAC-signed string emailed by /signin-link. Pass it in the x-customer-token header (preferred) or ?token=... query string. The `calendarFeedUrl` is a stable per-customer iCal subscription that includes their upcoming class enrollments — paste into Apple/Google/Outlook for read-only sync.

Query

  • token (optional)Alternative to the x-customer-token header.
curl 'https://alerabooking.com/api/customer/me?token=Y3VzdC12MXwy...' \
  -H 'x-alera-key: alr_sclp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
  -H 'Origin: https://your-site.com'

Response (200)

{
  "client": { "id": "cli-...", "name": "Jane", "email": "jane@example.com" },
  "studio": { "handle": "sculpt-co", "displayName": "Sculpt Pilates Co.", "timezone": "America/Chicago" },
  "calendarFeedUrl": "https://alerabooking.com/api/cal/abc123...ics",
  "enrollments": [
    {
      "id": "enr-...",
      "status": "Confirmed",
      "bookedAt": "2026-05-13T20:33:25.624Z",
      "cancelToken": "djF8...",
      "class": {
        "id": "class-7a2c",
        "name": "Pilates Class",
        "scheduledAt": "2026-05-15T12:00:00.000Z",
        "durationMin": 50
      }
    }
  ],
  "packages": [
    {
      "id": "pkg-...",
      "packageTypeName": "5-Class Pack",
      "sessionsRemaining": 4,
      "sessionsTotal": 6,
      "expiresAt": "2026-11-10T00:00:00.000Z"
    }
  ]
}

Errors

  • 401 Token missing, malformed, or expired
  • 403 Token's providerId doesn't match the API key's provider
  • 404 Customer or studio record not found

Section 04

Errors

Every non-2xx response is JSON of the form:

{ "error": "Invalid or revoked API key." }

Some responses carry additional hints (e.g. waitlistAvailable: true on 409 when a class is full; alreadyRedeemed: true on a duplicate gift-card commit). The error field is always present. Status code is the source of truth.

StatusWhen it fires
400Validation failed (bad JSON, missing required field, out-of-range value).
401Missing/invalid API key, or a signed token failed verification (bad signature, expired, wrong id).
403Origin not in the key's allowedOrigins, or the key/token doesn't scope to the requested provider.
404Resource not found, or scoped to a different tenant.
409Conflict — class full, tip already collected, package no credits left.
410Resource was cancelled or has already moved past the operable state.
412Precondition failed — usually means the pro hasn't finished Stripe Connect onboarding.
429Rate-limit exceeded. See the table below.
502Downstream failure (Stripe, internal RPC). Safe to retry with backoff.

Section 05

Rate limits

Per-IP, per-route, sliding 60s window. Exceeded calls return429with a retry-after hint in the error message. Per-key quotas are on the roadmap.

EndpointLimit (req/min/IP)
GET /api/classes60
GET /api/classes/:id120
POST /api/classes/:id/book10
POST /api/classes/:id/waitlist10
POST /api/enrollments/:id/cancel10
POST /api/packages/start20
POST /api/gift-cards/start20
POST /api/gift-cards/redeem30
POST /api/tip/start20
POST /api/customer/signin-link5
GET /api/customer/me30

Section 06

Payments & Stripe Connect

  • When a class has priceCents > 0 and the provider has Stripe Connect onboarded, /book returns a checkoutUrl. Redirect the customer to it.
  • Stripe handles card collection, PCI compliance, identity verification, and payout to the provider's bank. Alera takes a platform fee of zero today.
  • The enrollment is created in "pending payment" state at /book time. Our webhook stamps it as paid + increments enrolledCounton successful capture (Stripe's checkout.session.completed).
  • Refunds issued via /cancel hit Stripe on the connected account; the response includes the Stripe refund id.
  • Gift-card purchases use the same Direct-Charge model. Redemption (/api/gift-cards/redeem) is a ledger debit on Alera's side — no Stripe call — so it works for free.
  • Tips create a separate Direct-Charge against the appointment's connected account. Tips and the original booking can refund independently.

Section 07

Support

Questions, missing fields, integration gotchas — email team@alerabooking.com and we'll get back the same day during early access.

The API is in Beta. Endpoint shapes, query params, and response fields are stable but additive — we add fields freely, never remove or rename until a clean major-version cut.

By integrating you agree to our terms and privacy policy.

Ready to embed?

Get a key from Profile → Settings → API keys.

Already have early access? Sign in