Skip to content

Developers · API and webhooks

Your data is not locked in a box. API, webhooks, calendars.

Workout OS and Workout Studio follow the same conventions: a key-authenticated REST API for reading and writing, signed webhooks, an event feed and ICS calendar feeds. Full access on every paid plan, read-only on the free plan. We do not keep the API for enterprise tiers.

REST API

Members or clients, memberships or packs, payments, classes and bookings, attendance. Cursor pagination, updated_since for incremental sync, amounts in minor units.

Signed webhooks

Per-event subscriptions, HMAC SHA-256 signatures, 7 delivery attempts, a dead-letter box and one-click resend.

Calendars (ICS)

Secret calendar URLs for trainers and members: classes and appointments appear in Google, Apple or Outlook calendars.

OpenAPI 3.1

A machine-readable spec served by the API itself without a key, generated from the same schemas the API validates with.

What each plan includes

The only difference between plans is the rate limit. Write scopes and webhooks are on every paid plan.

PlanAPIWebhooksRequests / min / key
Free (gym and studio)Read-onlyNo (poll the event feed)60
Gym, Pro · Solo, StudioFull (read + write)Yes600
Chain · Studio+Full (read + write)Yes1200

Moving back to the free plan deletes nothing: write keys keep reading, writes return 403 plan_read_only and webhooks pause.

Scopes

Create a key in Settings → Integrations. It is shown once and only a SHA-256 hash is stored. Send it as Authorization: Bearer; a key only ever sees its own business.

Workout OS (wosk_…)

read:members
Members, door access feed, member.* and success.* events
read:contact
Adds phone and email to members (never for privacy-restricted members)
read:memberships
Memberships and membership.* events
read:payments
Payments and membership amounts
read:classes
Class types, sessions, bookings and booking.* events
read:attendance
Check-ins and attendance.recorded events
write:members
Create and update members
write:payments
Record manual payments: cash, card (POS), transfer
write:bookings
Book and cancel classes
write:events
Door system entry events

Workout Studio (wstk_…)

read:clients
Clients (without contact details)
read:contact
Phone and email; look up clients by phone or email
read:packages
Packs and pack types
read:bookings
Services, trainers, sessions, bookings, workshops
read:payments
Payments; pack and ticket amounts
write:clients
Create and update clients
write:bookings
Create and cancel bookings
write:payments
Record manual payments: cash, card (POS), transfer

Health data (forms, answers, measurements), staff notes, birth dates and photos are never returned by the API.

Events

Every event is written in the same database transaction as the change (outbox), so a saved change never loses its event. Instead of webhooks you can poll the event feed (kept 30 days, on every plan).

Workout OS

  • member.created
  • member.updated
  • membership.created
  • membership.renewed
  • membership.frozen
  • membership.expired
  • membership.cancelled
  • payment.recorded
  • booking.created
  • booking.cancelled
  • attendance.recorded
  • access.status_changed
  • success.case_opened

Workout Studio

  • client.created
  • client.updated
  • package.sold
  • package.renewed
  • package.expired
  • booking.created
  • booking.cancelled
  • booking.attended
  • booking.no_show
  • payment.recorded
  • event_ticket.created
  • event_ticket.cancelled
  • success.case_opened
Event envelope (same for both products)
{
  "id": "0b6f0a52-6a1e-4a8e-9f1e-3c4f0d3a2b11",
  "type": "payment.recorded",
  "created_at": "2026-10-23T14:02:00.000Z",
  "tenant_id": "730c3ef8-…",
  "api_version": "2026-10-23",
  "data": { "payment": { "id": "…", "amountKurus": 50000, "currency": "TRY", "method": "cash" } }
}
Headers
X-WorkoutOS-Event: payment.recorded
X-WorkoutOS-Delivery: <delivery uuid>
Idempotency-Key: <event id>
X-WorkoutOS-Signature: t=1761228120,v1=<hex>

Verifying the signature

v1 = HMAC_SHA256(secret, "<t>.<raw body>") as lowercase hex. Verify on the raw body bytes with a constant-time comparison, and reject timestamps older than 5 minutes.

  • The signing secret (whsec_…) is shown once and can be rotated at any time; during a rotation the header can carry more than one v1
  • Idempotency-Key = the event id: identical for every endpoint and every retry, so de-duplicate on it
  • Deliveries can arrive more than once and out of order: order by created_at
Node.js
import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(secret, rawBody, header, toleranceSec = 300) {
  const t = /(?:^|,)t=(\d+)/.exec(header ?? "")?.[1];
  const sigs = [...(header ?? "").matchAll(/v1=([0-9a-f]{64})/g)].map((m) => Buffer.from(m[1], "hex"));
  if (!t || sigs.length === 0 || Math.abs(Date.now() / 1000 - Number(t)) > toleranceSec) return false;
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
  return sigs.some((s) => s.length === expected.length && timingSafeEqual(s, expected));
}

Delivery guarantees

  • No 2xx within 10 seconds? We retry after 1 min, 5 min, 30 min, 2 h, 6 h and 24 h (7 attempts)
  • Then the delivery goes to the dead-letter box; resend one delivery or all failed ones in one click
  • If an endpoint keeps failing for 1 hour the owner is alerted; after 3 days of continuous failure it is turned off automatically
  • An integration health screen shows 24 h / 7 d success rates, pending and failed counts and the last error code

Idempotency-Key on writes

Every write needs an Idempotency-Key header (1–255 visible characters, for example your own order id). Keys are kept for 24 hours per business.

  • Same key + same request: the first response is returned again with Idempotent-Replayed: true
  • Same key + different body: 422 idempotency_key_reused
  • Two concurrent requests with the same key: the second waits for the first, one record is written
  • A failed request does not consume the key: fix it and retry with the same key

Rate limits

Every response carries the IETF RateLimit headers; over the limit you get 429 rate_limited with Retry-After.

RateLimit-Limit: 600
RateLimit-Remaining: 597
RateLimit-Reset: 41
RateLimit-Policy: 600;w=60

Calendar feeds (ICS)

Trainers add their own classes and sessions, members and clients their own bookings, to any calendar app. On every plan.

  • RFC 5545, times in UTC, the business time zone in X-WR-TIMEZONE
  • Cancelled bookings as STATUS:CANCELLED, waitlist as TENTATIVE
  • The last 30 days to the next 180 days
  • The URL is secret; resetting it invalidates the old one at once

Door and turnstile systems (gyms)

We sell no hardware; a gym's existing door system connects at software level. Access status comes from the same rule as a manual check-in at the front desk.

  • Access list: allowed, expired, frozen, debt, blocked
  • Incremental changes and the access.status_changed webhook
  • Write entry events from the door: they feed visit history, automations and reports
  • An allowed-members CSV for manual upload

Documentation

The OpenAPI 3.1 spec is served without a key at /v1/external/openapi.json on your business's API address, shown in Settings → Integrations. Public developer documentation is being prepared; until then, write to us for example requests and setup help.

  • Generate a client with Postman, Insomnia or openapi-generator
  • Zapier and Make: connect through webhooks
  • Error shape: { statusCode, code, message, fields? }, message in the business's language

FAQ

Which plans include the API?

Every paid plan has full access (read, write and webhooks). The free plan is read-only: you can read data and poll the event feed. The only other difference between plans is the rate limit.

Does the API cost extra?

No. The API, webhooks and calendar feeds are included in the plan price.

Can I connect my door or turnstile system?

Yes, at software level. Your door system reads the access list and changes through the API or webhooks and reports entries back. We do not sell or install hardware.

Does health data leave through the API?

No. Health forms, measurements, staff notes, birth dates and photos are never returned. Contact details need a separate scope.

What if a webhook cannot be delivered?

It is retried 7 times, then moves to the dead-letter box and can be resent from the panel. Events also stay in the event feed for 30 days.

Does it connect to my accounting software?

Not directly. Reports → Accounting exports a journal, a transaction list, or Xero and QuickBooks bank CSV files. It is not an e-invoicing integration.

Demo

See it with your own data.

Gym, studio or both? We will set up a 20-minute walkthrough using your own packs and client list. No commitment.