openapi: 3.2.0 info: title: SparkyFitness Exercise Stats API version: 1.0.0 description: API documentation for the SparkyFitness application, providing a comprehensive guide to all available endpoints. Have caution using the API directly, as improper use may lead to data loss or corruption. Also note that the API is subject to change without notice due to heavy development, so always refer to the latest documentation for up-to-date information. It might have flaw and due to vite/nginx internal proxy actual end point accessed via front end URL might be different than hitting them directly on the server. contact: name: SparkyFitness Support servers: - url: https://{host}/api description: Self-hosted SparkyFitness instance (the operator supplies the host). The upstream spec declares the relative base "/api". variables: host: default: sparkyfitness.example.com description: Hostname of your own SparkyFitness deployment. SparkyFitness is self-hosted; there is no vendor-operated API host. security: - apiKeyAuth: [] tags: - name: Exercise Stats paths: /exercise-stats/summary: get: summary: Get multi-interval exercise totals, trends, and period-over-period comparison tags: - Exercise Stats security: - apiKeyAuth: [] /exercise-stats/query: post: summary: Interrogate and filter activities (e.g. query all Half Marathons by distance/pace/date) tags: - Exercise Stats security: - apiKeyAuth: [] /exercise-stats/prs: get: summary: Get personal records (PRs) matrix across distance milestones and 1RMs description: 'Returns one cardio record per (sportGroup, distance standard) pair. sportGroup is the record boundary, so a hike and a walk compete for the same walk record, while sport describes the winning activity itself. Records are only comparable within a sportGroup — a 1 km walk is slower than a 1 mile run — so group by sportGroup rather than render a flat list. ' tags: - Exercise Stats security: - apiKeyAuth: [] parameters: - in: query name: userId schema: type: string format: uuid description: Target user; defaults to the authenticated user. Requires reports permission for anyone else. - in: query name: unitSystem schema: type: string enum: - metric - imperial default: metric description: Controls distance and pace formatting in the response. responses: '200': description: Personal records matrix. content: application/json: schema: $ref: '#/components/schemas/ExercisePRMatrixResponse' '400': description: Target User ID is required, or unitSystem is not metric/imperial. '403': description: Forbidden — no reports permission for the requested user. /exercise-stats/matched-courses: get: summary: Get grouped matched courses and repeated loop activities tags: - Exercise Stats security: - apiKeyAuth: [] parameters: - in: query name: userId schema: type: string format: uuid description: Target user; defaults to the authenticated user. Requires reports permission for anyone else. - in: query name: unitSystem schema: type: string enum: - metric - imperial default: metric description: Controls distance and pace formatting in the response. responses: '200': description: Repeated courses with their recent activities. content: application/json: schema: $ref: '#/components/schemas/MatchedCoursesResponse' '400': description: Target User ID is required, or unitSystem is not metric/imperial. '403': description: Forbidden — no reports permission for the requested user. components: schemas: ExercisePRMatrixResponse: type: object properties: cardioPRs: type: array description: One entry per (sportGroup, distance standard) pair, ordered by sport group then distance. items: $ref: '#/components/schemas/ExercisePersonalRecordItem' strength1RMs: type: array items: type: object properties: exerciseName: type: string estimatedOneRMKg: type: number weightKg: type: number reps: type: number achievedAt: type: string ExercisePersonalRecordItem: type: object description: One best effort. sportGroup is the record boundary — efforts compete only within the same group, so a hike and a walk contend for the same walk record — while sport classifies the winning activity itself. properties: id: type: string description: Stable id in the form pr--. category: type: string description: Legacy alias of sport, retained for older clients. sport: type: string enum: - running - cycling - walking - hiking - swimming - rowing - fitness_equipment - strength - other description: Canonical sport of the winning activity, named after the ANT+/FIT SDK sport enum. Descriptive only — it does not define which efforts competed for this record. Absent on servers predating per-sport records. sportGroup: type: string enum: - run - ride - walk - swim - other description: 'The record boundary: one record is kept per (sportGroup, distanceStandard) pair, and hiking folds into walk. Group by this field to render the matrix.' sportConfidence: type: string enum: - declared - inferred description: declared when the sport came from the provider's own enum, inferred when it was derived from notes or the activity name. distanceStandard: type: string enum: - 1k - 1mi - 5k - 10k - 15k - half_marathon - marathon - custom description: Milestone distance band this record belongs to. label: type: string description: Display label, e.g. "Half Marathon (21.1 km)". bestTimeSeconds: type: number formattedTime: type: string description: e.g. "1:42:15". avgPaceSecondsPerKm: type: number formattedPace: type: string description: e.g. "4:50 /km" or "7:47 /mi". activityId: type: string activityName: type: string achievedAt: type: string description: Calendar day the record was set (YYYY-MM-DD). MatchedCourseGroup: type: object description: A repeated route, grouped by activity name, with its recent runs of that course. properties: courseId: type: string courseName: type: string category: type: string description: Raw entry category as stored. sport: type: string enum: - running - cycling - walking - hiking - swimming - rowing - fitness_equipment - strength - other description: Sport derived from the course name and category. Absent on servers predating per-sport records. totalDistanceMeters: type: number avgDistanceFormatted: type: number activityCount: type: number bestTimeSeconds: type: number bestPaceFormatted: type: string recentActivities: type: array items: type: object properties: activityId: type: string activityName: type: string entryDate: type: string durationMinutes: type: number avgPaceFormatted: type: string avgHeartRate: type: - number - 'null' MatchedCoursesResponse: type: object properties: courses: type: array items: $ref: '#/components/schemas/MatchedCourseGroup' securitySchemes: apiKeyAuth: type: apiKey in: header name: x-api-key description: API key authentication via x-api-key header. x-provenance: generated: '2026-08-27' method: derived source: https://github.com/CodeWithCJ/SparkyFitness — assembled from the project's own swagger-jsdoc configuration (SparkyFitnessServer/config/swagger.ts) and the 419 @swagger JSDoc blocks in SparkyFitnessServer/routes/**, using the same scan paths and the same cookieAuth->apiKeyAuth post-processing the server applies. This is the identical document a running instance serves at GET /api/api-docs/json (Swagger UI at /api/api-docs/swagger, ReDoc at /api/api-docs/redoc). note: 'Not fetched from a live host: SparkyFitness is self-hosted and the project operates no public instance, so the contract can only be read from the source that generates it. Upstream sets no operationIds; paths+methods are the stable identifiers.' upstream_version: server package.json 1.6.4 (release v1.6.4, 2026-08-27)