{"openapi":"3.0.3","info":{"title":"OPEN Public API","version":"1.0.0","description":"Read-only REST API over a studio's own OPEN data (locations, schedule,\nclasses, students, memberships). Every request is authenticated by a\nstudio-scoped API key and is isolated to that studio's data. All\nendpoints are GET. Errors share one envelope: { \"error\": \u003ccode\u003e,\n\"message\": \u003ctext\u003e }. A per-API-key rate limit (default 60 requests per\n60 seconds) returns 429 with a Retry-After header."},"servers":[{"url":"https://app.openmanagerapp.com","description":"OPEN production API host"}],"security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"tags":[{"name":"Locations","description":"The studio's locales / branches."},{"name":"Schedule","description":"Classes over a date range and single-class detail."},{"name":"Students","description":"Student roster search, counts and profiles."},{"name":"Memberships","description":"A student's memberships and studio-wide expiring memberships."},{"name":"Meta","description":"Machine-readable API description."}],"paths":{"/api/v1/openapi.json":{"get":{"tags":["Meta"],"summary":"This OpenAPI 3.0 description (the machine-readable manual).","description":"Public and unauthenticated — the ONLY route on this surface that needs no API key.","operationId":"getOpenapi","security":[],"responses":{"200":{"description":"The OpenAPI 3.0 document.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/v1/locations":{"get":{"tags":["Locations"],"summary":"List the studio's locales / branches.","operationId":"listLocations","responses":{"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"200":{"description":"The studio's locales.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Location"}}}}}}}},"/api/v1/schedule":{"get":{"tags":["Schedule"],"summary":"Classes over a date range (max 31 days).","operationId":"getSchedule","parameters":[{"name":"date_from","in":"query","required":true,"description":"Start date (inclusive), YYYY-MM-DD. Must be on or before date_to.","schema":{"type":"string","format":"date"}},{"name":"date_to","in":"query","required":true,"description":"End date (inclusive), YYYY-MM-DD. Range must be 31 days or fewer.","schema":{"type":"string","format":"date"}}],"responses":{"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"200":{"description":"Classes in range.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Schedule"}}}},"422":{"$ref":"#/components/responses/InvalidParams"}}}},"/api/v1/classes/{id}":{"get":{"tags":["Schedule"],"summary":"Single-class detail (counts only, no student PII).","operationId":"getClass","parameters":[{"$ref":"#/components/parameters/ClassId"}],"responses":{"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"200":{"description":"Class detail.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClassDetail"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/students":{"get":{"tags":["Students"],"summary":"Search the (non-archived) student roster.","operationId":"searchStudents","parameters":[{"name":"query","in":"query","required":true,"description":"Accent-insensitive name OR email match.","schema":{"type":"string"}},{"name":"active","in":"query","required":false,"description":"Filter on the studio-facing active-member boolean.","schema":{"type":"boolean"}},{"name":"page","in":"query","required":false,"description":"Page number (25 per page).","schema":{"type":"integer","minimum":1,"default":1}}],"responses":{"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"200":{"description":"A page of matching students.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudentSearchResult"}}}},"422":{"$ref":"#/components/responses/InvalidParams"}}}},"/api/v1/students/active_count":{"get":{"tags":["Students"],"summary":"Count of active students.","operationId":"countActiveStudents","responses":{"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"200":{"description":"Active-student count.","content":{"application/json":{"schema":{"type":"object","required":["active_count"],"properties":{"active_count":{"type":"integer"}}}}}}}}},"/api/v1/students/{id}":{"get":{"tags":["Students"],"summary":"Full profile for one student (PII, notes, injury, membership summary).","operationId":"getStudent","parameters":[{"$ref":"#/components/parameters/StudentId"}],"responses":{"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"200":{"description":"Student profile.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudentProfile"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/students/{student_id}/memberships":{"get":{"tags":["Memberships"],"summary":"List one student's memberships / passes (up to 50, active first).","operationId":"listStudentMemberships","parameters":[{"$ref":"#/components/parameters/StudentIdForMemberships"}],"responses":{"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"200":{"description":"The student's memberships.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudentMemberships"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/v1/expiring_memberships":{"get":{"tags":["Memberships"],"summary":"Studio-wide memberships expiring within N days (soonest first).","operationId":"findExpiringMemberships","parameters":[{"name":"within_days","in":"query","required":true,"description":"Window in days from now (studio timezone). Positive integer, 365 or fewer.","schema":{"type":"integer","minimum":1,"maximum":365}}],"responses":{"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"200":{"description":"Expiring memberships.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpiringMemberships"}}}},"422":{"$ref":"#/components/responses/InvalidParams"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key supplied as `Authorization: Bearer \u003capi_key\u003e` (preferred)."},"apiKeyHeader":{"type":"apiKey","in":"header","name":"X-API-KEY","description":"API key supplied as `X-API-KEY: \u003capi_key\u003e` (accepted as a fallback to bearer)."}},"parameters":{"ClassId":{"name":"id","in":"path","required":true,"description":"Class (yclass) id.","schema":{"type":"integer"}},"StudentId":{"name":"id","in":"path","required":true,"description":"Student id.","schema":{"type":"integer"}},"StudentIdForMemberships":{"name":"student_id","in":"path","required":true,"description":"Student id whose memberships to list.","schema":{"type":"integer"}}},"responses":{"Unauthorized":{"description":"Missing key (`missing_api_key`) or invalid/revoked key (`invalid_api_key`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Unknown id, or an id belonging to another studio (`not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InvalidParams":{"description":"Bad or missing query/path parameters (`invalid_params`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Per-API-key rate limit exceeded (`rate_limited`). Default limit: 60 requests per 60 seconds.","headers":{"Retry-After":{"description":"Seconds until the rate-limit window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error","message"],"properties":{"error":{"type":"string","description":"Machine-readable error code.","enum":["missing_api_key","invalid_api_key","not_found","invalid_params","rate_limited","internal_error"]},"message":{"type":"string","description":"Human-readable detail."}}},"Location":{"type":"object","required":["id","title","priority","is_main"],"properties":{"id":{"type":"integer"},"title":{"type":"string","nullable":true},"priority":{"type":"integer","nullable":true},"is_main":{"type":"boolean"}}},"TeacherRef":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer"},"name":{"type":"string","nullable":true}}},"RoomRef":{"type":"object","nullable":true,"required":["id","name","capacity"],"properties":{"id":{"type":"integer"},"name":{"type":"string","nullable":true},"capacity":{"type":"integer","nullable":true}}},"Schedule":{"type":"object","required":["date_start","date_end","classes"],"properties":{"date_start":{"type":"string","format":"date"},"date_end":{"type":"string","format":"date"},"classes":{"type":"array","items":{"$ref":"#/components/schemas/ScheduleClass"}}}},"ScheduleClass":{"type":"object","required":["id","name","date","start","end","color","teacher","room","capacity","booked_count","spots_left","fill_pct","is_online","is_onsite","locale_id"],"properties":{"id":{"type":"integer"},"name":{"type":"string","nullable":true},"date":{"type":"string","format":"date","nullable":true},"start":{"type":"string","nullable":true},"end":{"type":"string","nullable":true},"color":{"type":"string","nullable":true,"description":"Class colour (yclass.clr)."},"teacher":{"type":"object","nullable":true,"required":["id","name"],"properties":{"id":{"type":"integer"},"name":{"type":"string","nullable":true}}},"room":{"$ref":"#/components/schemas/RoomRef"},"capacity":{"type":"integer","nullable":true},"booked_count":{"type":"integer"},"spots_left":{"type":"integer","nullable":true,"description":"null when the studio allows overbooking or capacity is unset."},"fill_pct":{"type":"number","nullable":true,"description":"booked/capacity*100, capped at 100; null when no capacity."},"is_online":{"type":"boolean","nullable":true},"is_onsite":{"type":"boolean","nullable":true},"locale_id":{"type":"integer","nullable":true}}},"ClassDetail":{"type":"object","required":["id","name","date","start","end","teachers","room","capacity","roster_count","spots_left","fill_pct","waitlist_count","is_online","is_onsite","locale_id"],"properties":{"id":{"type":"integer"},"name":{"type":"string","nullable":true},"date":{"type":"string","format":"date","nullable":true},"start":{"type":"string","nullable":true},"end":{"type":"string","nullable":true},"teachers":{"type":"array","items":{"$ref":"#/components/schemas/TeacherRef"}},"room":{"$ref":"#/components/schemas/RoomRef"},"capacity":{"type":"integer","nullable":true},"roster_count":{"type":"integer"},"spots_left":{"type":"integer","nullable":true},"fill_pct":{"type":"number","nullable":true},"waitlist_count":{"type":"integer"},"is_online":{"type":"boolean","nullable":true},"is_onsite":{"type":"boolean","nullable":true},"locale_id":{"type":"integer","nullable":true}}},"StudentSearchResult":{"type":"object","required":["students","page","per_page","total_entries","total_pages"],"properties":{"students":{"type":"array","items":{"$ref":"#/components/schemas/StudentSummary"}},"page":{"type":"integer"},"per_page":{"type":"integer","description":"Always 25."},"total_entries":{"type":"integer"},"total_pages":{"type":"integer"}}},"StudentSummary":{"type":"object","required":["id","name","email","active","phone","photo","is_archived"],"properties":{"id":{"type":"integer"},"name":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"active":{"type":"boolean","nullable":true},"phone":{"type":"string","nullable":true},"photo":{"type":"string","nullable":true},"is_archived":{"type":"boolean","description":"Always false (search excludes archived)."}}},"StudentProfile":{"type":"object","required":["id","name","email","phone","bd","dni","photo","active","is_archived","can_book","marketing_opt_out","note","injury","notes","memberships_summary"],"properties":{"id":{"type":"integer"},"name":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"bd":{"type":"string","nullable":true,"description":"Birth date."},"dni":{"type":"string","nullable":true,"description":"National id / document number."},"photo":{"type":"string","nullable":true},"active":{"type":"boolean","nullable":true},"is_archived":{"type":"boolean"},"can_book":{"type":"boolean","nullable":true},"marketing_opt_out":{"type":"boolean","nullable":true},"note":{"type":"string","nullable":true},"injury":{"$ref":"#/components/schemas/Injury"},"notes":{"type":"array","items":{"$ref":"#/components/schemas/StudentNote"}},"memberships_summary":{"$ref":"#/components/schemas/MembershipsSummary"}}},"StudentNote":{"type":"object","required":["id","title","body","note_type","tag","date","created_at"],"properties":{"id":{"type":"integer"},"title":{"type":"string","nullable":true},"body":{"type":"string","nullable":true},"note_type":{"type":"string","nullable":true},"tag":{"type":"string","nullable":true},"date":{"type":"string","format":"date","nullable":true},"created_at":{"type":"string","format":"date-time","nullable":true}}},"Injury":{"type":"object","required":["studio_note","client_reported"],"properties":{"studio_note":{"type":"string","nullable":true,"description":"Studio-entered injury note."},"client_reported":{"type":"string","nullable":true,"description":"Client-reported injuries (only if a linked client account exists)."}}},"MembershipsSummary":{"type":"object","required":["is_member","active_count","active"],"properties":{"is_member":{"type":"boolean","nullable":true},"active_count":{"type":"integer"},"active":{"type":"array","items":{"type":"object","description":"Minimal active-pass summary (LiveFacts passes)."}}}},"StudentMemberships":{"type":"object","required":["student_id","memberships"],"properties":{"student_id":{"type":"integer"},"memberships":{"type":"array","items":{"$ref":"#/components/schemas/Membership"}}}},"Membership":{"type":"object","required":["id","name","note","active","start","student_id","end","classes_left","purchase_date","is_workshop_ticket","start_first_day","pt","is_archived","price","total_classes","renewable","membership","subscription_status","payment_intent_id","stripe_account_id","is_enrolment","starts_on_use","price_id","product_id","yclasses","ticket_type"],"properties":{"id":{"type":"integer"},"name":{"type":"string","nullable":true},"note":{"type":"string","nullable":true},"active":{"type":"boolean","nullable":true},"start":{"type":"string","nullable":true},"student_id":{"type":"integer","nullable":true},"end":{"type":"string","nullable":true},"classes_left":{"type":"integer","nullable":true,"description":"Unlimited passes use a sentinel negative value."},"purchase_date":{"type":"string","nullable":true,"description":"ISO date-time string or empty string."},"is_workshop_ticket":{"type":"boolean","nullable":true},"start_first_day":{"type":"boolean","nullable":true},"pt":{"type":"string","nullable":true,"description":"Payment type."},"is_archived":{"type":"boolean","nullable":true},"price":{"type":"number","nullable":true},"total_classes":{"type":"integer","nullable":true},"renewable":{"type":"boolean","nullable":true},"membership":{"type":"boolean","nullable":true},"subscription_status":{"type":"string","nullable":true},"payment_intent_id":{"type":"string","nullable":true},"stripe_account_id":{"type":"string","nullable":true},"is_enrolment":{"type":"boolean","nullable":true},"starts_on_use":{"type":"boolean","nullable":true},"price_id":{"type":"string","nullable":true},"product_id":{"type":"string","nullable":true},"yclasses":{"type":"array","items":{"$ref":"#/components/schemas/MembershipYclass"}},"ticket_type":{"type":"object","nullable":true,"required":["id","name"],"properties":{"id":{"type":"integer"},"name":{"type":"string","nullable":true}}}}},"MembershipYclass":{"type":"object","required":["id","name","date","start"],"properties":{"id":{"type":"integer"},"name":{"type":"string","nullable":true},"date":{"type":"string","format":"date","nullable":true},"start":{"type":"string","nullable":true}}},"ExpiringMemberships":{"type":"object","required":["within_days","count","memberships"],"properties":{"within_days":{"type":"integer"},"count":{"type":"integer"},"memberships":{"type":"array","items":{"$ref":"#/components/schemas/ExpiringMembership"}}}},"ExpiringMembership":{"type":"object","required":["ticket_id","student","ticket_type_name","end","classes_left","renewable","subscription_status","pending_cancellation_at"],"properties":{"ticket_id":{"type":"integer"},"student":{"type":"object","nullable":true,"required":["id","name"],"properties":{"id":{"type":"integer"},"name":{"type":"string","nullable":true}}},"ticket_type_name":{"type":"string","nullable":true},"end":{"type":"string","nullable":true},"classes_left":{"type":"integer","nullable":true},"renewable":{"type":"boolean","nullable":true},"subscription_status":{"type":"string","nullable":true},"pending_cancellation_at":{"type":"string","format":"date-time","nullable":true}}}}}}