GymFlexa for developers

Drive your gym from your own code: a REST API with scoped keys and per-key rate limits over gyms, members, appointments, services and payments, and signed webhooks when something changes.

API keys

Get a key and authenticate

The GymFlexa REST API lets your own backend work with the same data the dashboard shows: your gyms, the members enrolled in them, the appointments on their calendars, the services you sell and the payments you take.

API keys are issued to an organization by one of its owners, and a key can be narrowed to a single gym. Its secret is shown once, when the key is created — store it somewhere safe. The key itself carries the tenant, so the organization and the gym never have to be sent by the caller and a gym-scoped key can never reach another gym.

Authenticate every request with HTTP Basic auth carrying only the key secret, base64-encoded, in the Authorization header.

# The Authorization header is HTTP Basic auth carrying only the key secret,
# with no username and no colon.
Authorization: Basic $(printf %s YOUR_API_KEY_SECRET | base64)

Every endpoint lives under https://api.gymflexa.com. Requests made with a key are rate limited per key; going over the limit returns 429. A key missing the scope an endpoint requires is refused with 403.

Quick start

Your first three calls

Find your gym, list the members enrolled in it, then read a week of its appointments.

# List the gyms of your organization
curl https://api.gymflexa.com/api/gyms \
  -H "Authorization: Basic $(printf %s YOUR_API_KEY_SECRET | base64)"

# List the members of one gym, asking for a few fields only
curl "https://api.gymflexa.com/api/members?gymId=GYM_ID&fields=name,surname,email" \
  -H "Authorization: Basic $(printf %s YOUR_API_KEY_SECRET | base64)"

# Read the appointments of that gym in a time range
curl "https://api.gymflexa.com/api/appointments?gymId=GYM_ID&startTime=2026-01-01T00:00:00.000Z&endTime=2026-01-08T00:00:00.000Z" \
  -H "Authorization: Basic $(printf %s YOUR_API_KEY_SECRET | base64)"

Browse the full API reference — every endpoint with its parameters, request body, responses and required scope.

CLI

Command-line interface

The same gyms, members, services, appointments and payments are available from your terminal through the gymflexa CLI. Install it globally with npm, or run it ad hoc with npx.

Authentication is one command: gymflexa login opens your browser to sign in to your GymFlexa account and stores a session for later commands — no API key to paste. Headless scripts export GYMFLEXA_API_KEY instead and skip the login step entirely.

# Install once, globally
npm install -g gymflexa
# or run it ad hoc without installing
npx gymflexa --help

# No account yet? Create one from the terminal
gymflexa signup --email founder@example.com

# Or log in — opens your browser and stores a session
gymflexa login

# The gyms of your organization
gymflexa gyms list

# The members of one gym, a few fields only
gymflexa members list --gymId GYM_ID --fields name,email,phone

# A week of appointments, as parseable JSON
gymflexa appointments list --gymId GYM_ID \
  --startTime 2026-01-01T00:00:00.000Z \
  --endTime 2026-01-08T00:00:00.000Z --json

Every subcommand accepts --json for parseable output, and gymflexa schema prints the whole command tree as JSON. The CLI is open source at github.com/gymflexa/cli and published as gymflexa on npm. Run any command with --help to see its options.

Scopes

Least privilege by default

Each key carries a list of scopes, so an integration that only needs to read your schedule never gets the ability to change it. New keys start read-only; widen them explicitly. A request whose key is missing the scope an endpoint requires is refused with 403.

  • gyms:readList the gyms of your organization and read one of them.
  • gyms:writeCreate, update and delete gyms.
  • members:readList your members and read a single member.
  • members:writeCreate, update and delete members.
  • appointments:readList appointments by gym, member or time range, and read a single one.
  • appointments:writeBook, reschedule, update and cancel appointments.
  • services:readList the services you offer and read a single service.
  • services:writeCreate, update and delete services.
  • payments:readList payments and read a single payment.
  • payments:writeRecord, update and delete payments.

MCP connector

Review gym operations in ChatGPT and Claude

Connect the production GymFlexa MCP endpoint with OAuth. Every read stays inside the organization authorized by your GymFlexa account and requires the gymflexa:read scope.

{
  "mcpServers": {
    "gymflexa": {
      "type": "http",
      "url": "https://mcp.gymflexa.com/mcp"
    }
  }
}

Administrative read-only tools

  • list_gymsFind an owned gym for a follow-up read.
  • get_gymReview safe scheduling configuration.
  • get_gym_availabilitySummarize bookable capacity for the fixed fourteen-day window.
  • get_gym_schedule_summaryCount scheduled records by UTC day without returning appointments.
  • get_gym_payment_summaryReview aggregate recorded payment status and totals without moving funds.
  • list_servicesReview bounded service-catalog names and prices.
  • show_gym_overviewRender a bounded administrative overview card.

The connector does not expose member identities or contacts, individual appointments or payments, attendance or service history, trainer data, free-text notes, decline reasons, card or processor data, or re-identifying links between records. It cannot book, edit, message, charge, refund, transfer, or delete anything.

The public connector never queries member records. Schedule and payment results are non-person-linked administrative aggregates: they contain no member identifier, individual booked time, attendance record, transaction, or record linkage.

Agent Skills

Teach your coding agent GymFlexa

GymFlexa ships Agent Skills — guides following the agentskills.io standard that teach coding agents how to work with gyms, members, appointments, services and payments through the gymflexa CLI and the MCP connector, instead of guessing at commands and tools.

# Install the GymFlexa skills into your coding agent
npx skills add gymflexa/skills

One command installs the skills into Claude Code, Cursor, Codex, Gemini CLI and any other agent that follows the Skills standard. The CLI also bundles the same guides, version-matched to the commands it ships: gymflexa skills get <name> prints one on demand.

The skills are open source at github.com/gymflexa/skills. Claude users can also install the GymFlexa Claude plugin, which bundles the connector together with the read-only gym-operations skill: github.com/gymflexa/claude-plugin.

Webhooks

Signed webhooks

Subscribe an endpoint of yours to a gym and GymFlexa POSTs the events you picked to your server as they happen. Subscriptions are managed on /api/webhooksubscriptions with an operator session token, and the signing secret is returned once, when the subscription is created.

  • member.createdA member was added.
  • member.updatedA member was edited, or their email bounced.
  • member.deletedA member was deleted.
  • appointment.createdAn appointment was booked.
  • appointment.updatedAn appointment was rescheduled, edited, or reviewed by the member.
  • appointment.deletedAn appointment was canceled or deleted.
  • payment.createdA payment was recorded.
POST https://your-server.com/gymflexa-webhook
X-Gymflexa-Event: member.created
X-Gymflexa-Signature: t=1719000000,v1=<hmac-sha256 hex>
Content-Type: application/json

{
  "event": "member.created",
  "timestamp": 1719000000,
  "data": { "...": "..." }
}

Verify the signature

Every delivery carries an X-Gymflexa-Signature header of the form t=timestamp,v1=signature, where the signature is an HMAC-SHA256 of timestamp.body keyed by the subscription secret. Recompute it over the raw body and compare before trusting the payload. The event name is repeated in the X-Gymflexa-Event header.

import crypto from 'node:crypto'

// body must be the RAW request body, byte for byte
function verify(header, body, secret) {
  const [t, v1] = (header || '').split(',').map(part => part.split('=')[1])
  if (!t || !v1) return false

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${t}.${body}`)
    .digest('hex')

  // timingSafeEqual throws on a length mismatch, so a malformed signature
  // has to be rejected before the comparison rather than by it.
  if (v1.length !== expected.length) return false

  return crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))
}

Delivery is one best-effort attempt with a five second timeout and no retries, so respond 2xx quickly and do the work asynchronously. An endpoint that fails twenty times in a row is disabled automatically and has to be re-enabled.

Start building