# CoachLayer API Reference > The fitness coaching brain, as an API. Training and recovery data in; coaching your users act on, out. 8 LLM endpoints behind one API key, metered in credits. This file is generated from the OpenAPI 3.1 contract (v1.0.0) and the public pricing schedule; regenerate with `npm run docs:build`; do not edit by hand. Status: live sandbox. Base URL: `https://coachlayer.justzon.com`. Self-serve signup (`POST /v1/signup`, no auth, no card) issues a test key with 300 free credits a month; the sandbox runs in test mode. Live keys (`cl_live_`) open with the paid tiers. ## Authentication Bearer API key on every call except `GET /health` and `POST /v1/signup`: `Authorization: Bearer cl_test_…`. Keys are hashed at rest and shown once at mint time; revoke and re-mint from the dashboard at will. ## Errors One envelope everywhere: `{"error": {"type", "code", "message", "request_id"}}`. - `authentication_error` (401): Missing, malformed or revoked API key. - `invalid_request_error` (400): Body fails validation; the message names the field. - `billing_error` (402 / 403): credit_limit_exhausted (Sandbox hard stop) or endpoint_not_in_tier (e.g. Form Check on Build). - `rate_limit_error` (429): Tier rpm exceeded; Retry-After header tells you when to retry. - `upstream_error` (502): Model upstream failed. You were not charged. - `capacity_error` (503): free_tier_paused (shared free-sandbox budget exhausted for the month, paid tiers unaffected) or gateway_unavailable (transient backend outage, retry shortly). Failed upstream calls (502) are never charged. ## Metering & rate limits Every metered response carries: - `X-Credits-Charged`: Credits charged for this call, length surcharges included. - `X-Credits-Overage`: Portion of the charge billed as overage (0 while inside the allowance). - `X-Credits-Remaining`: Included credits remaining in the current billing period. - `X-Request-Id`: Correlation id, echoed in error envelopes and the usage ledger. Rate limits are per-tier requests-per-minute over a fixed 60s window; 429 responses carry `Retry-After` (seconds). ## Credit schedule - `/v1/chat` (Coach Chat): 2 credits / message - `/v1/brief` (Readiness Brief (SITREP)): 3 credits / brief - `/v1/coach-insight` (Coach Insight): 3 credits / insight - `/v1/workout-analysis` (Analyze Workout): 6 credits / analysis - `/v1/adaptive-session` (Adaptive Program): 8 credits / day rewrite - `/v1/weekly-report` (Weekly Report): 12 credits / report - `/v1/form-check` (Form Check): 25 credits / clip (+25 per extra 30 seconds beyond 30) - `/v1/import` (Magic Import): 40 credits / import (+20 per extra 12 weeks beyond 12) ## Tiers - Sandbox: $0/mo, 300 credits/mo, hard stop at allowance, 10 rpm. No card required. - Build: $39/mo, 2,500 credits/mo, $0.025/credit overage, 30 rpm, no form_check. Email support. - Launch: $99/mo, 7,000 credits/mo, $0.022/credit overage, 60 rpm. Webhooks, usage dashboard. - Growth: $299/mo, 24,000 credits/mo, $0.018/credit overage, 300 rpm. Analytics, priority support. - Scale: $999/mo, 95,000 credits/mo, $0.014/credit overage, 1000 rpm. White-label, SSO, DPA. - Enterprise: custom pricing, 250,000 credits/mo, custom overage, 2000 rpm. Annual, committed discounts, fine-tunes on your data, dedicated infra. ## POST /v1/chat: Coach chat (2 credits/message) Cost: 2 credits / message. Request fields: - `messages` (array of object, required): Conversation so far; the last entry must be role "user". Trimmed to the last 12. - `workout_context` (object): Live workout state that grounds the reply. - `lang` (string): ISO 639-1 Default: "en". Example request body: ```json { "messages": [ { "role": "user", "content": "Bench felt heavy at 80kg for 3x8. Should I go up next session?" } ], "workout_context": { "workout_name": "Push Day A", "completed_sets_summary": "Bench 3x8 @ 80kg, OHP 3x10 @ 40kg", "active_exercise_name": "Bench Press", "primary_goal": "strength" }, "lang": "en" } ``` Example response: ```json { "reply": "Nice work on the bench sets. Given 3x8 at 80kg felt solid, load 82.5kg for your next set and keep the same rep target. Stop one rep shy of failure." } ``` ## POST /v1/brief: Daily readiness brief, aka SITREP (3 credits) Cost: 3 credits / brief. Request fields: - `biometrics` (object, required) - `schedule` (object, required) - `completed_today` (object) - `user_profile` (object) - `lang` (string): Default: "en". Example request body: ```json { "biometrics": { "sleep_score": 82, "hrv_value": 68, "resting_heart_rate": 52, "subjective_readiness": 4 }, "schedule": { "has_workout": true, "workout_name": "Push Day A", "is_heavy": true }, "user_profile": { "age": 31, "weight_kg": 78, "height_cm": 181, "gender": "male" }, "lang": "en" } ``` Example response: ```json { "title": "Primed for Push Day", "short_recommendation": "Green light: train as planned.", "status": "normal", "message": "Sleep score 82 and HRV trending up: recovery is on your side today. Your push session is a go at full intensity. Cap the last pressing movement two reps shy of failure to bank tomorrow." } ``` ## POST /v1/coach-insight: Coach-facing athlete insight (3 credits) Cost: 3 credits / insight. Same input contract as /v1/brief, framed for the coach reviewing an athlete. Request fields: - `biometrics` (object, required) - `schedule` (object, required) - `lang` (string): Default: "en". Example request body: ```json { "biometrics": { "sleep_score": 64, "hrv_value": 51, "resting_heart_rate": 58 }, "schedule": { "has_workout": true, "workout_name": "Pull Day B" }, "lang": "en" } ``` Example response: ```json { "title": "Adherence is the story this week", "short_recommendation": "Flag the missed pull sessions before they become a pattern.", "status": "normal", "message": "This athlete hit 3 of 5 planned sessions and skipped both pull days. Volume on push muscles is 40% above their 30-day average; worth rebalancing next week before elbow niggles show up." } ``` ## POST /v1/workout-analysis: Post-workout analysis (6 credits) Cost: 6 credits / analysis. Request fields: - `workout` (object, required): The completed session: name, exercises[] (name, muscle_group, tracking_type, sets[] with weight_kg/reps/rpe/distance_meters/duration_seconds), duration_seconds, notes, debrief_tags/debrief_note, average_bpm, total_distance_meters. Optional session_type override. - `history` (array of object): Prior sessions (date, name, total_volume_kg, exercises[]); enables PR detection and e1RM deltas (needs >= 2 prior data points per lift). - `athlete` (object): age, weight_kg, goal, experience (beginner|intermediate|advanced), sessions_completed, streak_days, days_since_last_session. - `coach_directives` (string): Free-text instructions from the athlete's human coach, applied by the analysis. - `warning_counts` (object): 7-day repeat-warning counts (low_sleep, low_readiness, pace_fade, sessions_in_window); dampens or escalates repeated warnings. - `options` (object): progressive_overload: boolean (default false). True emits per-exercise load prescriptions. - `lang` (string): Default: "en". Example request body: ```json { "workout": { "name": "Lower A", "duration_seconds": 3600, "exercises": [ { "name": "Back Squat", "muscle_group": "quads", "tracking_type": "weight_reps", "sets": [ { "weight_kg": 100, "reps": 5, "rpe": 8 }, { "weight_kg": 100, "reps": 5, "rpe": 8.5 } ] } ] }, "history": [ { "date": "2026-08-28", "name": "Lower A", "total_volume_kg": 5200 } ], "athlete": { "age": 31, "weight_kg": 78, "goal": "strength", "experience": "intermediate", "sessions_completed": 142, "streak_days": 12 }, "options": { "progressive_overload": false }, "lang": "en" } ``` Example response: ```json { "headline": "Strong session with a pacing caveat", "rating": 8, "key_insights": [ { "type": "volume", "label": "Total volume", "metric": "+12% vs last legs day", "tone": "positive" }, { "type": "pacing", "label": "Rest drift", "metric": "rests grew 45s by the final block", "tone": "warning" } ], "next_step": "Keep the added back-squat set; tighten rests to 2:30 on accessories.", "summary": "Volume progression is on track and bar speed held up across the top sets. The only leak is pacing: the back half of the session drifted long, which mutes the conditioning stimulus.", "strengths": [ "Progressive overload on squat", "Consistent depth across all sets" ], "improvements": [ "Rest discipline on accessories" ], "session_type": "strength", "schema_version": 2 } ``` ## POST /v1/adaptive-session: Adaptive session synthesis (8 credits) Cost: 8 credits / day rewrite. Request fields: - `duration_minutes` (15 | 30 | 45 | 60 | 90, required) - `equipment_preset` ("full_gym" | "home_gym" | "hotel" | "custom", required) - `allowed_equipment` (array of string) - `signals` (object): Recovery + load signals (ACWR per muscle, 7/30-day volume, sleep, HRV, soreness…). - `personal_records` (array of object) - `goal` (string | null) - `notes` (string | null) - `lang` (string): Default: "en". Example request body: ```json { "duration_minutes": 45, "equipment_preset": "full_gym", "signals": { "sleep_hours": 7.5, "hrv_trend": "up", "soreness": { "hamstrings": 6 } }, "personal_records": [ { "exercise_name": "Back Squat", "e1rm": 140 } ], "goal": "strength", "lang": "en" } ``` Example response: ```json { "session_name": "Adaptive Lower Body (45 min)", "reasoning": "ACWR shows quads fresh but hamstrings near the top of their band; the session biases hinge volume down and keeps squat intensity, matching the 45-minute cap.", "estimated_volume_kg": 6450, "estimated_duration_minutes": 44, "exercises": [ { "library_exercise_name": "Back Squat", "order_index": 1, "block_label": "A", "target_sets": 4, "target_reps": 6, "target_weight_kg": 100, "rest_seconds": 180, "notes": "Top set at RPE 8.", "tracking_type": "weight_reps" }, { "library_exercise_name": "Romanian Deadlift", "order_index": 2, "block_label": "B", "target_sets": 3, "target_reps": 8, "target_weight_kg": 80, "rest_seconds": 150, "notes": "Reduced a set vs plan: hamstring ACWR 1.28.", "tracking_type": "weight_reps" } ] } ``` ## POST /v1/weekly-report: Weekly training report (12 credits) Cost: 12 credits / report. Request fields: - `week` (object, required): Aggregates of the week under review (start_date, end_date, total_volume_kg, total_duration_seconds, session_count, avg_rpe, total_distance_meters). - `previous_week` (object): Same aggregates for the prior week; enables trends (deltas are computed server-side and clamped). - `goal` (object): category: endurance|strength|crossfit|hypertrophy|weight_loss|general; focus: free text. - `movements` (array of object): Per-lift context (name, top_set, e1rm_kg, previous_e1rm_kg, sets_done, target_sets); this is what turns narration into coaching. - `sessions` (array of object): Session summaries of the week (date, name, volume_kg, distance_meters, duration_seconds, avg_rpe). - `wellness` (object): daily: array of {date, sleep_hours, sleep_quality, stress_level, soreness_level, energy_level}. - `nutrition` (array of object): Daily macros ({date, kcal, protein_g, carb_g, fat_g}). - `check_in` (object): Weekly check-in (date, weight_kg, notes, qa[]). - `previous_directives` (array of object): Prior weeks' directives ({week_start_date, focus_area, directive}); prevents repetition. - `lang` (string): Default: "en". Example request body: ```json { "week": { "start_date": "2026-08-31", "end_date": "2026-09-06", "total_volume_kg": 42300, "total_duration_seconds": 14400, "session_count": 4, "avg_rpe": 7.6 }, "previous_week": { "start_date": "2026-08-24", "end_date": "2026-08-30", "total_volume_kg": 38800, "session_count": 4, "avg_rpe": 7.3 }, "goal": { "category": "strength", "focus": "squat 1RM" }, "movements": [ { "name": "Back Squat", "top_set": "120kg x 5", "e1rm_kg": 140, "previous_e1rm_kg": 136, "sets_done": 12, "target_sets": 12 } ], "lang": "en" } ``` Example response: ```json { "rank": "A", "headline": "Best training week in a month", "summary": "4 of 4 planned sessions completed, volume up 9% on rising intensity, and sleep held above 7h every night. One watch-out: Friday soreness spiked after the deadlift PR.", "metrics": { "volume": { "value": "42,300 kg", "trend": "up", "diff_percent": 9 }, "intensity": { "value": "7.6", "trend": "up", "diff_percent": 4, "unit": "RPE" }, "consistency": { "value": "100%", "trend": "flat", "diff_percent": 0 } }, "focus_area": "Recovery", "coach_directive": "Hold volume next week and bank the adaptation: no new PR attempts before Thursday.", "next_week_plan": [ { "title": "Deload hinge pattern", "detail": "RDLs at 80% of this week’s load." }, { "title": "Keep squat intensity", "detail": "Same top-set weight, one fewer back-off set." } ], "wins": [ "Deadlift PR 180kg", "Perfect session adherence" ] } ``` ## POST /v1/form-check: Video form check (25 credits ≤30s, +25 per extra 30s) Cost: 25 credits / clip (+25 per extra 30 seconds beyond 30). Request fields: - `video_url` (string, required) - `video_seconds` (number): Clip length; drives the length surcharge. Clips are analyzed at pinned low media resolution. - `exercise_name` (string | null) - `lang` (string): Default: "en". Example request body: ```json { "video_url": "https://cdn.example.com/clips/squat-topset.mp4", "video_seconds": 24, "exercise_name": "Back Squat", "lang": "en" } ``` Example response: ```json { "score": 78, "strengths": [ "Consistent bar path", "Solid brace at setup" ], "improvements": [ "Knees cave slightly on reps 4-5", "Depth just above parallel under fatigue" ], "recommendations": [ "Add a 2-second pause squat set at 60% to groove depth.", "Cue \"knees out\" on the final reps or drop 5% load." ], "detected_exercise": "Back Squat", "low_confidence": false } ``` ## POST /v1/import: Magic Import: parse a program from text/file (40 credits up to 12 weeks, +20 per extra 12) Cost: 40 credits / import (+20 per extra 12 weeks beyond 12). Request fields: - `text_content` (string): Raw program text (one of text_content / file_url / file_base64 required). - `file_url` (string): PDF/image/markdown source. - `file_base64` (string): Inline file content - `file_mime_type` (string): MIME type of file_base64 (application/pdf - `program_weeks` (number): Declared length; drives the length surcharge. - `import_type` ("full_program" | "routine" | "nutrition_import"): Default: "full_program". - `lang` (string): Default: "en". Example request body: ```json { "text_content": "Week 1\nDay 1: Squat 5x5 @ 75%, RDL 3x8, Plank 3x60s\nDay 2: Bench 5x5 @ 77.5%…", "program_weeks": 8, "import_type": "full_program", "lang": "en" } ``` Example response: ```json { "message": "Program parsed and structured successfully.", "import_type": "full_program", "is_finished": true, "imported_data": { "type": "full_program", "weeks_count": 8, "workouts_count": 32, "program_json": { "name": "Intermediate Strength Block", "weeks": 8, "days_per_week": 4, "sample_day": { "name": "Day 1: Squat focus", "exercises": [ { "name": "Back Squat", "sets": 5, "reps": 5, "intensity": "75-82% 1RM" } ] } } } } ``` ## POST /v1/signup: Self-serve sandbox signup (no auth, no card) Cost: free (public, unauthenticated). Request fields: - `name` (string, required): Account or project name (1-120 characters). - `email` (string, required): Contact address for the account. Example request body: ```json { "name": "Acme Fitness", "email": "dev@acme.fit" } ``` Example response: ```json { "tenant_id": "ten_9f2c", "name": "Acme Fitness", "tier": "sandbox", "api_key": "cl_test_k3H…", "note": "Store this key now: it is shown once and kept only as a hash. Sandbox is test-mode only." } ``` ## GET /v1/me: Current key's tenant, tier and limits Cost: free (authenticated). Example response: ```json { "tenant_id": "ten_9f2c", "name": "Acme Fitness", "tier": "growth", "monthly_credits": 24000, "rpm": 300, "blocked_endpoints": [] } ``` ## GET /v1/usage: Current billing period usage Cost: free (authenticated). Example response: ```json { "period": "2026-09", "tier": "growth", "credits_included": 24000, "credits_used": 1240, "credits_remaining": 22760, "by_endpoint": { "chat": { "calls": 310, "credits": 620, "tokens_in": 868000, "tokens_out": 198400 }, "weekly_report": { "calls": 40, "credits": 480, "tokens_in": 392000, "tokens_out": 108000 } } } ``` ## GET /health: Liveness (public) Cost: free (public, unauthenticated). ## MCP server The gateway ships a Model Context Protocol server (stdio transport) exposing the 8 intelligence endpoints plus `usage` and `me` as tools, with input schemas taken from the OpenAPI contract. Run it with `npm run mcp` (env: `COACHLAYER_API_KEY`, `COACHLAYER_BASE_URL`, default `https://coachlayer.justzon.com`). ## Buyer FAQ The questions engineering leads ask before integrating, answered as on the [home page](/#faq). ### Which models power CoachLayer? A versioned, continuously evaluated mix per endpoint, with automatic cross-provider failover. We deliberately do not publish the mix: it changes as models improve, while the contract you integrate against does not. You never manage provider keys, quotas, or migrations; when models improve, your endpoints improve behind a stable schema, and the benchmark shows the result. ### Why not just call OpenAI ourselves? You can, after you rebuild months of fitness-specific prompts, output schemas, eval suites, failover and edge cases, and commit to maintaining them as models churn. Trying CoachLayer costs one afternoon and $0. Keep whichever wins. ### What data do I send, and do you train on it? JSON: workouts, wearable scores, goals, whatever context the endpoint needs. No camera except Form Check (an uploaded clip). Your traffic is never used to train or improve ZON’s consumer products. Sandbox logs retained ≤30 days; DPA available from Scale. ### Are you GDPR-ready? We’re an EU company. So are we: CoachLayer is built by ZON, in France. DPA from Scale tier, EU data-residency options on the Enterprise roadmap, and we’ll answer your security questionnaire like people who’ve filled one in before. ### What about latency? Structured endpoints stream where it matters (chat, import). Real numbers belong in docs, not marketing. We publish measured p50/p95 per endpoint from production telemetry, not invented figures. ### What languages do you output? English and French at native coaching quality today (French is where the engine grew up). Other languages on request. Ask before you assume no. ### Can we white-label or fine-tune? White-label and SSO from Scale. Enterprise adds fine-tuned models on your calibration data, which also means the coaching gets better on your athletes in a way no generic model call will. ### How stable are the schemas? Endpoints are versioned (/v1/). Breaking changes ship as new versions with a 12-month deprecation window. Your integration will not rot under you. ## FAQ by page The questions answered on the benchmark, comparison, guide and calculator pages, verbatim, under the page that answers them. ### [Coaching quality benchmark](/benchmark) #### Why machine-checked scores instead of model-graded ones? Because a model grading a model inherits both models’ blind spots. The cost of that choice is real and we accept it: our checks measure whether an output does its job, not perceived coaching brilliance. An output can pass every check and still be bland; the qualitative defects we found by reading outputs one by one (data restated instead of interpreted, for example) became new checks. #### Why do two endpoints score exactly 100%? Because both were audited and reworked in July 2026, and the measurement confirms the audit landed. We considered shipping further changes anyway just to announce a gain and declined: a gain over a 100% baseline would be a fabricated number. The honest reading is that the older code eras score 76.1% and 70 to 78% on the same checks, which is also published above. #### Can I reproduce this? No, and that is deliberate. The corpus is our production traffic and the harness embeds the internals this product is made of, so neither is published. What we publish instead is the part a benchmark usually hides: the weaknesses, the endpoints with no number yet, and the corrections to our own instrument. Design partners get deeper access, under NDA, for the endpoints they build on. #### What changes in the next version? Three things, in order: instrumentation on the endpoints that have none (so the three unmeasured endpoints get numbers instead of apologies), a from-scratch baseline run for the raw-model comparison, and continuous scoring so this page tracks production instead of a September 2026 campaign. ### [CoachLayer vs building on a frontier model](/compare/build-vs-buy) #### Frontier models keep improving. Does the layer become worthless? Model upgrades genuinely absorbed one of our defects: a migration took unsolicited actions from 65% to zero, and we published that. What upgrades do not absorb: contracts (a better model still needs to know your schema), safety coherence rules, and the measurement that tells you what a migration changed. Better models make the layer thinner, and they make knowing-what-changed more valuable, not less. #### Is a wrapper around someone else’s model defensible at all? A naked wrapper is not, and the market is right to be cynical about them. What we sell is not model access: it is eight endpoint contracts extracted from a shipped training product, the validators behind them, and a production benchmark with our weaknesses printed on it. #### We already prototyped coaching with a system prompt. It looks fine. Ours looked fine too. Then reading 183 real outputs found two analyses that told an athlete they were at elevated injury risk and, in the next sentence, to push intensity. One percent of outputs, invisible in any demo, and exactly the kind of thing that ends up in a screenshot. Fine-in-a-demo and safe-at-volume are different claims; only measurement separates them. ### [How much a fitness coaching API costs](/guides/fitness-coaching-api-cost) #### Why credits instead of per-token pricing? Because your finance team cannot forecast tokens and neither can we. A readiness brief is 3 credits whether the model needed a short or a long context that day; volatility in token consumption is our problem, not your invoice’s. #### What happens when I run out of credits? Paid tiers meter overage at a published per-credit rate that decreases with tier size; the sandbox stops hard instead of billing. Exact overage rates are on the pricing table. #### Is there really a free way to evaluate this? Yes: the sandbox tier includes 300 credits with no card, enough to test every endpoint against your real data shapes before a single pricing conversation. ### [How to integrate a coaching API](/guides/fitness-api-integration) #### Can I call a coaching API directly from my mobile app? No. The key is a server credential: shipping it in a mobile binary means anyone who unzips your app can spend your credits. Call CoachLayer from your backend and let your app talk only to your backend. You almost certainly already have that backend, because neither HealthKit nor Health Connect exposes a server-side API, so your client is already uploading health data to you. #### How long does the integration actually take? The first authenticated call takes minutes: self-serve signup issues a test key with no card, and every endpoint takes JSON you already hold. The real work is not the HTTP call, it is deciding which athlete context you send and where the responses are cached. Teams ship one endpoint in an afternoon and the rest over a sprint. #### What athlete data do I have to send, and what do you store? You send only the context the endpoint needs, passed in the request body: recent sessions, the current program, sleep and load signals where you have them. There is no onboarding sync, no account linking and no long-lived copy of your user table on our side. Requests are metered in the usage ledger by request id, not by personal profile. #### How do I meter and reconcile credits from my side? Every metered response carries X-Credits-Charged, X-Credits-Overage, X-Credits-Remaining and X-Request-Id. Persist the request id and the charge next to your own event, and your ledger reconciles against ours line by line without a support ticket. Rate limits are per tier: 10 requests per minute on the sandbox, 60 on Launch, 300 on Growth. #### What breaks in production, and how should I handle it? Five failure modes matter: 402 when the allowance is spent (design a graceful degrade), 429 when you exceed the tier rate limit (honour Retry-After and queue batch work), 502 upstream failures (never charged, retry once with backoff), 503 capacity (retry shortly), and 403 endpoint-not-in-tier, which is a deploy-time bug you should assert against in CI rather than discover at runtime. ### [Fitness coaching API cost calculator](/calculator) #### How is a CoachLayer bill calculated? Every call costs a fixed number of credits set per endpoint, from 2 credits for a Coach Chat message to 40 for a Magic Import import. Each tier includes a monthly credit allowance. The calculator multiplies your volume per endpoint by those weights, prices every production tier your endpoints are allowed on, overage included, and recommends the cheapest. #### What happens when usage goes over the monthly credits? On paid tiers, extra credits are billed at the tier's overage rate, from $0.025 per credit on Build down to $0.014 on Scale. The free Sandbox has no overage: it stops at its 300-credit allowance. #### Is there a free tier to evaluate the API? Yes. The Sandbox tier includes 300 credits a month with no card required, covers all 8 endpoints under a non-production licence, and is rate limited to 10 requests a minute. Production traffic starts on the first paid tier. #### Do longer videos or programs cost more? Yes, for the two endpoints whose work scales with input length. Form Check costs 25 credits per clip with the first 30 seconds included, then 25 credits per extra 30 seconds. Magic Import costs 40 credits per import with the first 12 weeks included, then 20 credits per extra 12 weeks. Every other endpoint is a flat price per call. #### Will my invoice match this estimate? The credit weights and tier prices come from the same config file the billing engine reads, so the arithmetic is identical; the estimate is only as good as the volumes you enter. Billing is in USD. Euro figures are an indicative conversion for display.