DemoYou're exploring a sample gym. Nothing you do here is real, and everything resets overnight.

DEVELOPERS

The GymOS API

A REST API and an MCP server, so your own tools (or an AI agent) can run the gym. Included on Premium.

Authentication

Every request carries Authorization: Bearer <key>. Keys are created and revoked by an admin under API keys in the console, and the plaintext key is shown exactly once.

A key acts as a real user in your gym and can do exactly what that user can do; there is no separate permission set stored on the key. Give an integration an admin to reach everything; give it a member for self-service only. Changing that person's role changes what the key can do immediately, and removing it cuts the key off with nothing else to revoke.

curl -H "Authorization: Bearer gk_live_..." \
  https://your-gym.example.com/api/v1/members

Paging

No list endpoint returns everything it has. Every list response carries a meta alongside its data, and how you ask for the next part depends on what the list is.

Cursor paging — members, dues

Ask with `limit` (default 100, max 500). The response's `meta.nextCursor` is an opaque position: pass it back as `cursor` for the next page, and stop when it is null. Cursors are stable while rows are being written underneath you, which page numbers are not — a member paying a due mid-walk will not make you skip or repeat a row.

Windowed — class sessions

Ask with `from` and `to` (ISO datetimes). Left out, you get the next 30 days. A calendar is read by date range, not by row offset.

Capped feeds — attendance, device metrics

Newest first, `limit` default 100 and max 500. Walk backwards with `since`. `meta.truncated` is true when the limit was reached, which is your signal that there is more — never assume a full-looking page was the whole answer.

Errors and limits

Every error is { "error": { "code", "message" } } with a matching status. Each gym may make 300 requests a minute across all its keys combined, not per key.

CodeStatusWhen
UNAUTHENTICATED401Missing or unrecognised key.
FORBIDDEN403The acting-as user cannot do this.
NOT_FOUND404No such thing in your organization.
CONFLICT409The request clashes with current state — full session, already enrolled, out of stock.
VALIDATION_ERROR422The body or query didn't parse.
RATE_LIMITED429Over 300 requests a minute for the organization.

Endpoints

Everything is scoped to the key's own gym; there is no way to address another gym's data. The one exception is franchise analytics, which is called out below.

Members

  • GET/api/v1/members

    List the member directory, alphabetically, a page at a time.

    Query: limit (max 500, default 100), cursor. Pass meta.nextCursor to continue; null means the last page. Never includes checkInCode — that is the member's own kiosk secret.

  • POST/api/v1/members

    Attach a member profile to an existing user.

    Does not create a login. No domain function creates users outside signup.

  • GET/api/v1/members/{id}

    One member, with start date and authorized notes.

Attendance

  • GET/api/v1/attendance

    Org-wide check-in feed, newest first.

    Query: since (ISO datetime), limit (max 500, default 100). meta.truncated says there were more — walk forward with since.

  • POST/api/v1/attendance

    Record a manual check-in.

    Deducts a class-pack credit exactly as the admin UI does.

Memberships

  • GET/api/v1/membership-plans

    Active membership plans.

  • GET/api/v1/members/{id}/membership

    The member's active membership, or null.

  • POST/api/v1/members/{id}/membership

    Give the member a membership.

    Replaces any existing active one, same as the admin UI.

Billing

  • GET/api/v1/dues

    Dues, newest first, a page at a time.

    Query: status (pending/paid/waived), limit (max 500, default 100), cursor. Pass meta.nextCursor to continue; null means the last page.

  • POST/api/v1/dues

    Raise a due against a member.

  • POST/api/v1/dues/{id}/payments

    Record a manual or external payment.

Scheduling

  • GET/api/v1/class-sessions

    Upcoming scheduled sessions, within a date window.

    Query: from, to (ISO datetimes; default now to +30 days), limit (max 500, default 100). meta.truncated says the window held more than the limit — narrow it or raise the limit.

  • POST/api/v1/class-sessions/{id}/enrollments

    Enrol the key's acting-as user.

    Self-service only — a key cannot enrol somebody else.

  • DELETE/api/v1/class-sessions/{id}/enrollments

    Cancel that enrolment, releasing the spot to the waitlist.

Staff

  • GET/api/v1/staff

    Every staff and trainer profile.

  • POST/api/v1/staff

    Attach a staff profile to an existing user.

    Roles are still granted in the console only.

  • GET/api/v1/staff/{id}

    One staff profile, by user id.

Analytics

  • GET/api/v1/analytics/overview

    Active members, attendance and revenue snapshot.

  • GET/api/v1/analytics/at-risk-members

    Members flagged by the churn-risk heuristics.

  • GET/api/v1/analytics/class-engagement

    Session counts and average fill rate per class type.

  • GET/api/v1/analytics/member-progression

    Skill-evaluation coverage across active members.

  • GET/api/v1/analytics/trainer-effectiveness

    Per-trainer load, enrolment and evaluation metrics.

Franchises

  • GET/api/v1/franchises/{franchiseId}/analytics

    Aggregated metrics across every location in a franchise.

    The only endpoint that reaches beyond the key's own organization, and only when the acting-as user is an admin at every location in it.

Device integrations

  • GET/api/v1/device-integrations

    List integrations. The webhook secret is never re-shown.

  • POST/api/v1/device-integrations

    Create one.

    The response carries the webhook secret exactly once.

  • PATCH/api/v1/device-integrations/{id}

    Enable or disable an integration.

  • GET/api/v1/device-connections

    Member to external-user mappings.

  • POST/api/v1/device-connections

    Map a member to an external user id.

  • DELETE/api/v1/device-connections/{id}

    Revoke a mapping.

    Later events for that external id are reported unmatched, not stored.

  • GET/api/v1/device-metrics

    Normalized readings. Query: memberUserId, metric, since, limit (max 500, default 100). meta.truncated says there were more.

  • POST/api/v1/device-metrics

    Push events directly. Returns ingest counts, 202.

MCP server

/api/mcp speaks the Model Context Protocol. Point an MCP client at it with the same bearer key and an agent can act on your gym, under the same acting-as user, with the same permissions and the same rate limit as the REST API.

{
  "mcpServers": {
    "gymos": {
      "url": "https://your-gym.example.com/api/mcp",
      "headers": { "Authorization": "Bearer gk_live_..." }
    }
  }
}

23 tools:

list_members · get_member · create_member · list_attendance · record_attendance · list_membership_plans · get_member_membership · create_member_membership · list_dues · create_due · record_payment · list_class_sessions · enroll_in_class_session · cancel_class_enrollment · get_analytics_overview · get_at_risk_members · get_class_engagement · get_member_progression · get_trainer_effectiveness · list_staff · get_staff · create_staff · get_franchise_analytics_overview

Not in the API

Granting roles, taking card payments, and anything that would let a key reach outside its own gym. Those stay in the console on purpose. If you need something here that isn't, tell us. The surface grows deliberately rather than by accident.