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.
| Code | Status | When |
|---|---|---|
| UNAUTHENTICATED | 401 | Missing or unrecognised key. |
| FORBIDDEN | 403 | The acting-as user cannot do this. |
| NOT_FOUND | 404 | No such thing in your organization. |
| CONFLICT | 409 | The request clashes with current state — full session, already enrolled, out of stock. |
| VALIDATION_ERROR | 422 | The body or query didn't parse. |
| RATE_LIMITED | 429 | Over 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.