{"openapi":"3.1.0","info":{"title":"Jas Run Coach API","version":"0.2.0","description":"Private read-only running and training-plan API for Jas. Bearer-token authentication resolves each request to exactly one manually managed profile."},"servers":[{"url":"https://jas.til3s.co.uk"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"AthleteMetadata":{"type":"object","properties":{"id":{"type":"integer"},"profile_slug":{"type":"string"},"display_name":{"type":["string","null"]},"is_primary":{"type":"boolean"},"strava_athlete_id_available":{"type":"boolean"}}},"TrainingPlan":{"type":"object","properties":{"id":{"type":"string"},"planSlug":{"type":"string"},"planName":{"type":"string"},"versionNumber":{"type":"integer"},"source":{"type":["string","null"]},"notes":{"type":["string","null"]},"startsOn":{"type":"string","format":"date"},"endsOn":{"type":["string","null"],"format":"date"},"isActive":{"type":"boolean"},"importedAt":{"type":"string","format":"date-time"}}},"PlannedWorkout":{"type":"object","properties":{"id":{"type":"string"},"planVersionId":{"type":"string"},"date":{"type":"string","format":"date"},"planned_date":{"type":"string","format":"date"},"sport":{"type":"string"},"workoutType":{"type":["string","null"]},"workout_type":{"type":["string","null"]},"title":{"type":["string","null"]},"description":{"type":["string","null"]},"purpose":{"type":["string","null"]},"plannedDistanceKm":{"type":["number","null"]},"planned_distance_km":{"type":["number","null"]},"plannedDurationMinutes":{"type":["number","null"]},"planned_duration_minutes":{"type":["number","null"]},"intensity":{"type":["string","null"]},"target_intensity":{"type":["string","null"]},"weekCommencing":{"type":["string","null"],"format":"date"},"week_commencing":{"type":["string","null"],"format":"date"},"phase":{"type":["string","null"]},"runDay":{"type":["string","null"]},"run_day":{"type":["string","null"]},"terrain":{"type":["string","null"]},"notes":{"type":["string","null"]},"spreadsheetNotes":{"type":["string","null"]},"spreadsheet_notes":{"type":["string","null"]},"spreadsheetDone":{"type":["string","null"]},"spreadsheet_done":{"type":["string","null"]},"status":{"type":["string","null"]},"linkedRunId":{"type":["integer","null"]},"linked_run_id":{"type":["integer","null"]}}},"Run":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":["string","null"]},"date":{"type":["string","null"],"format":"date-time"},"localDate":{"type":["string","null"]},"distanceKm":{"type":["number","null"]},"movingTimeMinutes":{"type":["number","null"]},"elapsedTimeMinutes":{"type":["number","null"]},"paceMinPerKm":{"type":["number","null"]},"elevationGainMeters":{"type":["number","null"]},"averageHeartRate":{"type":["number","null"]},"maxHeartRate":{"type":["number","null"]},"has_hr_data":{"type":"boolean"},"averageCadence":{"type":["number","null"]},"averageSpeedMetersPerSecond":{"type":["number","null"]},"sufferScore":{"type":["number","null"]}}},"Summary":{"type":"object","properties":{"days":{"type":"integer"},"runCount":{"type":"integer"},"totalDistanceKm":{"type":"number"},"totalMovingTimeMinutes":{"type":"number"},"totalElevationGainMeters":{"type":"number"},"averageDistanceKm":{"type":"number"},"averagePaceMinPerKm":{"type":["number","null"]},"averageHeartRate":{"type":["number","null"]}}},"PlanRealityItem":{"type":"object","properties":{"status":{"type":"string","enum":["completed","moved","modified","missed","upcoming","ambiguous","unmatched_run"]},"plannedWorkout":{"anyOf":[{"$ref":"#/components/schemas/PlannedWorkout"},{"type":"null"}]},"run":{"anyOf":[{"$ref":"#/components/schemas/Run"},{"type":"null"}]},"dateDeltaDays":{"type":["integer","null"]},"distanceDeltaPercent":{"type":["number","null"]},"tracking":{"anyOf":[{"$ref":"#/components/schemas/SpreadsheetTrackingContext"},{"type":"null"}]},"notes":{"type":"array","items":{"type":"string"}}}},"SpreadsheetTrackingContext":{"type":"object","properties":{"spreadsheetDone":{"type":["string","null"]},"spreadsheet_done":{"type":["string","null"]},"doneHint":{"type":"string","enum":["done","not_done","blank","unknown"]},"done_hint":{"type":"string","enum":["done","not_done","blank","unknown"]},"hintedDistanceKm":{"type":["number","null"]},"hinted_distance_km":{"type":["number","null"]},"mismatchFlags":{"type":"array","items":{"type":"string"}},"mismatch_flags":{"type":"array","items":{"type":"string"}}}},"PlanRealitySummary":{"type":"object","properties":{"completed":{"type":"integer"},"moved":{"type":"integer"},"modified":{"type":"integer"},"missed":{"type":"integer"},"upcoming":{"type":"integer"},"ambiguous":{"type":"integer"},"unmatched_run":{"type":"integer"},"plannedPast":{"type":"integer"},"adherenceRate":{"type":["number","null"]},"coachingFrame":{"type":"string"}}},"PlanAdherence":{"type":"object","properties":{"days":{"type":"integer"},"plan":{"anyOf":[{"$ref":"#/components/schemas/TrainingPlan"},{"type":"null"}]},"summary":{"$ref":"#/components/schemas/PlanRealitySummary"},"plan_vs_actual":{"type":"array","items":{"$ref":"#/components/schemas/PlanRealityItem"}}}},"CoachWeeklySummary":{"type":"object","properties":{"week_start":{"type":"string","format":"date"},"week_end":{"type":"string","format":"date"},"phase":{"type":["string","null"]},"planned_run_count":{"type":["integer","null"]},"completed_run_count":{"type":["integer","null"]},"moved_run_count":{"type":["integer","null"]},"modified_run_count":{"type":["integer","null"]},"missed_run_count":{"type":["integer","null"]},"ambiguous_run_count":{"type":["integer","null"]},"unmatched_run_count":{"type":["integer","null"]},"planned_distance_km":{"type":["number","null"]},"actual_distance_km":{"type":["number","null"]},"long_run_planned_km":{"type":["number","null"]},"long_run_actual_km":{"type":["number","null"]},"adherence_status":{"type":"string","enum":["good","mixed","poor","unknown"]},"load_risk":{"type":"string","enum":["green","amber","red","unknown"]},"coaching_verdict":{"type":["string","null"]},"recommendation":{"type":["string","null"]},"data_limitations":{"type":"array","items":{"type":"string"}},"generated_at":{"type":"string","format":"date-time"},"is_stale":{"type":"boolean"},"source_latest_updated_at":{"type":["string","null"],"format":"date-time"}}},"SyncStatus":{"type":"object","properties":{"ok":{"type":"boolean"},"athlete":{"anyOf":[{"$ref":"#/components/schemas/AthleteMetadata"},{"type":"null"}]},"latest_run":{"anyOf":[{"type":"object","properties":{"id":{"type":"integer"},"start_date":{"type":["string","null"],"format":"date-time"},"distance_km":{"type":["number","null"]},"name":{"type":["string","null"]}}},{"type":"null"}]},"latest_sync":{"type":"object","properties":{"synced_at":{"type":["string","null"],"format":"date-time"},"hours_since_sync":{"type":["number","null"]},"source":{"type":["string","null"],"enum":["athlete_sync_status","run_updated_at","strava_token_updated_at",null]},"status":{"type":["string","null"]}}},"coverage":{"type":"object","properties":{"runs_synced_7d":{"type":"integer"},"runs_synced_30d":{"type":"integer"}}},"staleness":{"type":"string","enum":["fresh","aging","stale","unknown"]},"warnings":{"type":"array","items":{"type":"string"}}}},"SeasonContext":{"type":"object","properties":{"current_goal":{"type":["string","null"]},"current_goal_date":{"type":["string","null"],"format":"date"},"next_goal":{"type":["string","null"]},"next_goal_date":{"type":["string","null"],"format":"date"},"phase":{"type":"string","enum":["marathon_taper","marathon_recovery","ultra_base_or_build","ultra_taper","post_ultra_recovery","unknown"]},"days_to_current_goal":{"type":["integer","null"]},"days_to_next_goal":{"type":["integer","null"]}}},"CoachContext":{"type":"object","properties":{"athlete":{"anyOf":[{"$ref":"#/components/schemas/AthleteMetadata"},{"type":"null"}]},"sync_status":{"$ref":"#/components/schemas/SyncStatus"},"season_context":{"$ref":"#/components/schemas/SeasonContext"},"latest_run":{"anyOf":[{"$ref":"#/components/schemas/Run"},{"type":"null"}]},"recent_runs":{"type":"array","items":{"$ref":"#/components/schemas/Run"}},"this_week":{"type":"object"},"last_6_weeks":{"type":"array","items":{"type":"object"}},"training_week":{"type":"object"},"summary_28d":{"$ref":"#/components/schemas/Summary"},"active_plan":{"anyOf":[{"$ref":"#/components/schemas/TrainingPlan"},{"type":"null"}]},"upcoming":{"type":"object","properties":{"days":{"type":"integer"},"workouts":{"type":"array","items":{"$ref":"#/components/schemas/PlannedWorkout"}}}},"upcoming_workouts":{"type":"array","items":{"$ref":"#/components/schemas/PlannedWorkout"}},"this_week_planned_workouts":{"type":"array","items":{"$ref":"#/components/schemas/PlannedWorkout"}},"coach_weekly_summaries":{"type":"object","properties":{"current_week":{"anyOf":[{"$ref":"#/components/schemas/CoachWeeklySummary"},{"type":"null"}]},"previous_week":{"anyOf":[{"$ref":"#/components/schemas/CoachWeeklySummary"},{"type":"null"}]}}},"current_week_persisted_summary":{"anyOf":[{"$ref":"#/components/schemas/CoachWeeklySummary"},{"type":"null"}]},"previous_week_persisted_summary":{"anyOf":[{"$ref":"#/components/schemas/CoachWeeklySummary"},{"type":"null"}]},"plan_vs_reality":{"type":"object","properties":{"summary":{"$ref":"#/components/schemas/PlanRealitySummary"},"items":{"type":"array","items":{"$ref":"#/components/schemas/PlanRealityItem"}}}},"plan_vs_actual":{"type":"object","properties":{"summary":{"$ref":"#/components/schemas/PlanRealitySummary"},"items":{"type":"array","items":{"$ref":"#/components/schemas/PlanRealityItem"}}}},"coach_flags":{"type":"array","items":{"type":"string"}},"limits":{"type":"object","properties":{"notes":{"type":"array","items":{"type":"string"}}}},"data_limitations":{"type":"array","items":{"type":"string"}}}},"Error":{"type":"object","properties":{"error":{"type":"string"}}}}},"security":[{"bearerAuth":[]}],"paths":{"/api/plan/active":{"get":{"operationId":"getActivePlan","summary":"Get the active training plan and planned workouts.","responses":{"200":{"description":"Active plan and workouts.","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"anyOf":[{"$ref":"#/components/schemas/TrainingPlan"},{"type":"null"}]},"workouts":{"type":"array","items":{"$ref":"#/components/schemas/PlannedWorkout"}}}}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/plan/upcoming":{"get":{"operationId":"getUpcomingPlan","summary":"Get upcoming planned workouts.","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":90,"default":14}}],"responses":{"200":{"description":"Upcoming planned workouts.","content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"integer"},"plan":{"anyOf":[{"$ref":"#/components/schemas/TrainingPlan"},{"type":"null"}]},"workouts":{"type":"array","items":{"$ref":"#/components/schemas/PlannedWorkout"}}}}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/plan/week":{"get":{"operationId":"getPlanWeek","summary":"Get this week's planned workouts.","responses":{"200":{"description":"Current week of planned workouts.","content":{"application/json":{"schema":{"type":"object","properties":{"weekStart":{"type":"string","format":"date-time"},"weekEnd":{"type":"string","format":"date-time"},"plan":{"anyOf":[{"$ref":"#/components/schemas/TrainingPlan"},{"type":"null"}]},"workouts":{"type":"array","items":{"$ref":"#/components/schemas/PlannedWorkout"}}}}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/plan/adherence":{"get":{"operationId":"getPlanAdherence","summary":"Get plan-vs-actual workout matching.","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":365,"default":28}}],"responses":{"200":{"description":"Plan adherence context.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanAdherence"}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/sync/status":{"get":{"operationId":"getSyncStatus","summary":"Get private Strava sync freshness and latest-run status.","responses":{"200":{"description":"Sync freshness status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncStatus"}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/coach/context":{"get":{"operationId":"getCoachContext","summary":"Get compact coaching context with plan-vs-reality.","responses":{"200":{"description":"Coaching context briefing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoachContext"}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/coach/weekly-summary":{"get":{"operationId":"getCoachWeeklySummary","summary":"Get a persisted coach weekly summary.","parameters":[{"name":"week_start","in":"query","required":false,"schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Persisted coach weekly summary.","content":{"application/json":{"schema":{"type":"object","properties":{"week_start":{"type":"string","format":"date"},"week_end":{"type":"string","format":"date"},"summary":{"anyOf":[{"$ref":"#/components/schemas/CoachWeeklySummary"},{"type":"null"}]}}}}}},"401":{"description":"Missing or invalid bearer token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}