{"openapi":"3.1.0","info":{"title":"ExerciseBank API","version":"1","description":"1,492 studio-filmed exercises with video, stills, bilingual metadata and a substitute map."},"servers":[{"url":"https://exercisebank-one.vercel.app/v1"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"Your API key: eb_live_…"}},"schemas":{"Exercise":{"type":"object","properties":{"id":{"type":"string","example":"tc-2000"},"name":{"type":"string","example":"Ab slam"},"nameFr":{"type":"string"},"variant":{"type":"string","description":"Coach-style name: Region- Movement- Position- Equipment","example":"Abs- Slam"},"description":{"type":"string"},"descriptionFr":{"type":"string"},"media":{"type":"object","description":"Relative paths, not links. Signed URLs come from POST /media/urls.","properties":{"mp4":{"type":"string"},"still":{"type":"string"},"signed":{"type":"boolean"},"expires":{"type":"integer","description":"Unix seconds"},"sandbox":{"type":"boolean"}}},"primaryMuscles":{"type":"array","items":{"type":"string"}},"secondaryMuscles":{"type":"array","items":{"type":"string"}},"equipment":{"type":"array","items":{"type":"string"}},"movementPattern":{"type":"string"},"difficulty":{"type":"integer","minimum":1,"maximum":5},"loadedJoints":{"type":"array","items":{"type":"string"}},"impact":{"type":"string","enum":["low","moderate","high"]},"unilateral":{"type":"boolean"},"cues":{"type":"array","items":{"type":"string"}},"cuesFr":{"type":"array","items":{"type":"string"}},"aliases":{"type":"array","items":{"type":"string"}},"hash":{"type":"string","description":"Changes when any field changes; use it to sync"}}},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}},"paths":{"/meta":{"get":{"summary":"Catalogue version, counts, enums, sample ids, recent changes","responses":{"200":{"description":"OK"}}}},"/catalog":{"get":{"summary":"The whole catalogue (ETag = version; send If-None-Match)","responses":{"200":{"description":"OK"},"304":{"description":"Unchanged"}}}},"/changelog":{"get":{"summary":"Every version with ids added / changed / removed","responses":{"200":{"description":"OK"}}}},"/exercises":{"get":{"summary":"Search and filter","parameters":[{"name":"q","in":"query","schema":{"type":"string"},"description":"Text: name, French name, coach name, aliases"},{"name":"muscle","in":"query","schema":{"type":"string"},"description":"Primary or secondary muscle (comma-separated)"},{"name":"equipment","in":"query","schema":{"type":"string"},"description":"Gear the user HAS; bodyweight is always allowed (comma-separated)"},{"name":"pattern","in":"query","schema":{"type":"string"},"description":"Movement pattern (comma-separated)"},{"name":"maxDifficulty","in":"query","schema":{"type":"integer"}},{"name":"minDifficulty","in":"query","schema":{"type":"integer"}},{"name":"excludeJoints","in":"query","schema":{"type":"string"},"description":"Skip anything loading these joints (comma-separated)"},{"name":"impact","in":"query","schema":{"type":"string"},"description":"low, moderate, high (comma-separated)"},{"name":"unilateral","in":"query","schema":{"type":"boolean"}},{"name":"ids","in":"query","schema":{"type":"string"},"description":"Restrict to these ids (comma-separated)"},{"name":"limit","in":"query","schema":{"type":"integer","default":25,"maximum":100}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Exercise"}},"total":{"type":"integer"},"next":{"type":"integer","nullable":true}}}}}},"401":{"description":"Bad key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"A limit was hit"}}}},"/exercises/{id}":{"get":{"summary":"One exercise (data only)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"404":{"description":"Unknown id"}}}},"/exercises/{id}/substitutes":{"get":{"summary":"Ranked replacements with a reason each","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"excludeJoints","in":"query","schema":{"type":"string"},"description":"Skip substitutes loading these joints (comma-separated)"},{"name":"equipment","in":"query","schema":{"type":"string"},"description":"Gear the user has (comma-separated)"},{"name":"limit","in":"query","schema":{"type":"integer","default":10,"maximum":50}}],"responses":{"200":{"description":"OK"}}}},"/media/urls":{"post":{"summary":"Signed media URLs for up to 50 exercises on screen, for a named end user","parameters":[{"name":"X-End-User","in":"header","required":true,"schema":{"type":"string"},"description":"Opaque id of the person who will watch. Counts toward monthly end users."},{"name":"X-End-User-Role","in":"header","required":false,"schema":{"type":"string","enum":["coach"]},"description":"Mark a power user who builds programs: higher daily distinct-exercise cap, counts toward the plan's coach seats for the calendar month."}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"},"maxItems":50},"kinds":{"type":"array","items":{"type":"string","enum":["mp4","still"]},"description":"Default both"},"ttl":{"type":"integer","minimum":60,"maximum":3600,"default":900}},"required":["ids"]}}}},"responses":{"200":{"description":"OK"},"429":{"description":"Distinct-exercise cap for the day, or coach seats exceeded (sandbox)"}}}},"/feedback":{"post":{"summary":"Report a problem with an exercise (kind: issue) or ask for one to be added (kind: request). Repeats of an open item merge.","parameters":[{"name":"X-End-User","in":"header","required":false,"schema":{"type":"string"},"description":"Opaque id of the person who will watch. Counts toward monthly end users."},{"name":"X-End-User-Role","in":"header","required":false,"schema":{"type":"string","enum":["coach"]},"description":"Mark a power user who builds programs: higher daily distinct-exercise cap, counts toward the plan's coach seats for the calendar month."}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["kind","details"],"properties":{"kind":{"type":"string","enum":["issue","request"]},"details":{"type":"string","minLength":10,"maxLength":4000},"exerciseId":{"type":"string","description":"issue: required"},"categories":{"type":"array","items":{"type":"string","enum":["video_quality","video_broken","wrong_clip","movement_form","equipment","muscles","movement_pattern","difficulty","joints_impact","name","description_cues","translation","substitutes","duplicate","other"]},"description":"issue: required"},"relatedExerciseId":{"type":"string","description":"issue: required for substitutes and duplicate"},"media":{"type":"object","properties":{"kind":{"type":"string","enum":["mp4","still"]},"atSecond":{"type":"number"}}},"suggestions":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","enum":["name","nameFr","description","descriptionFr","cues","cuesFr","aliases","primaryMuscles","secondaryMuscles","equipment","loadedJoints","movementPattern","impact","difficulty","unilateral"]},"value":{}},"required":["field","value"]}},"hash":{"type":"string","description":"The record's hash in your copy"},"name":{"type":"string","description":"request: required"},"description":{"type":"string"},"equipment":{"type":"array","items":{"type":"string"},"description":"request: required"},"primaryMuscles":{"type":"array","items":{"type":"string"},"description":"request: required"},"secondaryMuscles":{"type":"array","items":{"type":"string"}},"movementPattern":{"type":"string"},"difficulty":{"type":"integer","minimum":1,"maximum":5},"unilateral":{"type":"boolean"},"similarTo":{"type":"array","items":{"type":"string"}},"useCase":{"type":"string"},"referenceUrl":{"type":"string","format":"uri"},"demand":{"type":"integer","minimum":1},"reporter":{"type":"object","properties":{"role":{"type":"string","enum":["coach","client","developer","other"]},"locale":{"type":"string"},"appVersion":{"type":"string"}}},"externalRef":{"type":"string"}}}}}},"responses":{"200":{"description":"Merged into an open item"},"201":{"description":"Created"},"400":{"description":"Missing or invalid field"},"404":{"description":"Unknown exercise"},"429":{"description":"More than 100 items per key per day"}}},"get":{"summary":"Your account's feedback","parameters":[{"name":"kind","in":"query","schema":{"type":"string","enum":["issue","request"]}},{"name":"status","in":"query","schema":{"type":"string"},"description":"new, reviewing, accepted, fixed, added, declined, duplicate (comma-separated)"},{"name":"exerciseId","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":25,"maximum":100}},{"name":"offset","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}}},"/feedback/{id}":{"get":{"summary":"One feedback item and its status","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"404":{"description":"Not on your account"}}}}}}