KinesteX

Workout Sessions API

Every completed workout session is stored server-side, tied to the userId the SDK was initialized with (sessions are persisted when the workout is launched with shouldSendStats: true). Fetch the history to build progress screens, completed-workout lists, or your own statistics page.


Two read endpoints share the same authentication:


EndpointReturns
GET /api/workout-sessionsPaginated session history for a user
GET /api/workout-sessions/{id}One session in full detail, including per-exercise results

Headers (both endpoints):


HeaderDescription
x-api-keyYour company API key (server-side only)
x-user-idThe user's id — always send it; it must match the SDK's userId

Optionally pass Language: es (or Accept-Language) to have planned_exercises[].title returned in that language.


List sessions


typescript
1GET https://data.kinestex.com/api/workout-sessions

Query params (all optional): page (default 1), limit (default 20, max 50), sort (default started_at_desc).


Response (trimmed to the most useful fields):


json
1{
2  "sessions": [
3    {
4      "id": 4978,
5      "content_title": "Fitness Lite",
6      "content_image_url": "https://cdn.kinestex.com/uploads%2F...webp",
7      "content_difficulty": "Medium",
8      "calories_burned": 1.03,
9      "actual_duration_seconds": 12,
10      "completion_percentage": 0.67,
11      "total_exercises": 10
12    }
13  ],
14  "pagination": { "page": 1, "limit": 20, "total": 1, "total_pages": 1, "has_more": false }
15}

Each session also carries planned_exercises[] (with per-exercise thumbnails and rep/time targets), accuracy_score, efficiency_score, total_mistakes, and timestamps.


Key rules:

  • The values are actuals: calories_burned is what the user really burned and actual_duration_seconds is real time spent (in seconds — format it; don't show "0 min" for short sessions).
  • completion_percentage (0–100, can be fractional like 0.67) is authoritative — never derive completion from exercise counts, since total_exercises includes untouched exercises.
  • Errors: 401 invalid x-api-key · 404 x-user-id not found for your company · 400 bad query param.

Get a single session


typescript
1GET https://data.kinestex.com/api/workout-sessions/{id}

Returns one workout session in full detail — including the per-exercise breakdown (exercises[]) that the list endpoint doesn't include. Use it to build your own session-detail or statistics page. {id} is the numeric session id from the list response, or from the SDK's workout_session_saved event (session_id).


bash
1curl "https://data.kinestex.com/api/workout-sessions/5124" \
2  -H "x-api-key: YOUR_API_KEY" \
3  -H "x-user-id: user-abc-123"

Response — the session object is returned directly (no wrapper):


json
1{
2  "id": 5124,
3  "created_at": "2026-08-04T09:47:31Z",
4  "updated_at": "2026-08-04T09:47:31Z",
5  "company_user_id": "user-abc-123",
6  "company_id": 42,
7  "integration_option": "workout",
8  "client_session_id": "9f4b6c2a-1d3e-4c5b-8a7f-2e6d9c0b4a11",
9  "content_id": "261",
10  "content_title": "Full Body Strength",
11  "content_image_url": "https://cdn.kinestex.com/uploads%2Fworkouts%2Ffull-body.webp",
12  "content_difficulty": "Medium",
13  "planned_exercises": [
14    {
15      "exercise_id": "122",
16      "title": "Squats",
17      "thumbnail_url": "https://cdn.kinestex.com/exercises/squat-thumbnail.jpg",
18      "reps_target": 12,
19      "time_target_seconds": null,
20      "exercise_type": "rep",
21      "language": "en"
22    },
23    {
24      "exercise_id": "87",
25      "title": "Plank",
26      "thumbnail_url": "https://cdn.kinestex.com/exercises/plank-thumbnail.jpg",
27      "reps_target": null,
28      "time_target_seconds": 60,
29      "exercise_type": "timer",
30      "language": "en"
31    },
32    {
33      "exercise_id": "95",
34      "title": "Push Ups",
35      "thumbnail_url": "https://cdn.kinestex.com/exercises/push-ups-thumbnail.jpg",
36      "reps_target": 12,
37      "time_target_seconds": null,
38      "exercise_type": "rep",
39      "language": "en"
40    }
41  ],
42  "started_at": "2026-08-04T09:30:00Z",
43  "completed_at": "2026-08-04T09:47:30Z",
44  "actual_duration_seconds": 1050,
45  "target_duration_seconds": 1080,
46  "completed_reps_count": 22,
47  "target_reps_count": 24,
48  "calories_burned": 10.8,
49  "completion_percentage": 94.44,
50  "accuracy_score": 88,
51  "efficiency_score": 84,
52  "total_mistakes": 6,
53  "total_exercises": 3,
54  "actual_hold_time_seconds": 52,
55  "target_hold_time_seconds": 60,
56  "readiness": null,
57  "post_workout_feedback": null,
58  "has_rep_based_exercises": true,
59  "has_ai_model": true,
60  "exercises": [
61    {
62      "id": 18211,
63      "created_at": "2026-08-04T09:47:31Z",
64      "updated_at": "2026-08-04T09:47:31Z",
65      "workout_session_id": 5124,
66      "exercise_index": 0,
67      "exercise_id": 122,
68      "reps_done": 12,
69      "reps_target": 12,
70      "time_spent_seconds": 45,
71      "time_target_seconds": null,
72      "calories": 4.5,
73      "average_accuracy": 91,
74      "rep_accuracy": [95, 88, 92, 90, 94, 89, 93, 91, 90, 92, 88, 90],
75      "mistakes": [{ "mistake": "straighten back", "count": 2 }],
76      "mistake_count": 2,
77      "perfect_position_time": 0,
78      "mistake_time_spent": 0,
79      "has_ai_model": true,
80      "exercise_type": "rep"
81    },
82    {
83      "id": 18212,
84      "created_at": "2026-08-04T09:47:31Z",
85      "updated_at": "2026-08-04T09:47:31Z",
86      "workout_session_id": 5124,
87      "exercise_index": 1,
88      "exercise_id": 87,
89      "reps_done": 0,
90      "reps_target": null,
91      "time_spent_seconds": 60,
92      "time_target_seconds": 60,
93      "calories": 3.2,
94      "average_accuracy": 87,
95      "rep_accuracy": null,
96      "mistakes": [{ "mistake": "hips too low", "count": 1 }],
97      "mistake_count": 1,
98      "perfect_position_time": 52,
99      "mistake_time_spent": 8,
100      "has_ai_model": true,
101      "exercise_type": "timer"
102    },
103    {
104      "id": 18213,
105      "created_at": "2026-08-04T09:47:31Z",
106      "updated_at": "2026-08-04T09:47:31Z",
107      "workout_session_id": 5124,
108      "exercise_index": 2,
109      "exercise_id": 95,
110      "reps_done": 10,
111      "reps_target": 12,
112      "time_spent_seconds": 40,
113      "time_target_seconds": null,
114      "calories": 3.1,
115      "average_accuracy": 85,
116      "rep_accuracy": [88, 84, 86, 85, 87, 83, 85, 84, 86, 82],
117      "mistakes": [{ "mistake": "elbows flaring", "count": 3 }],
118      "mistake_count": 3,
119      "perfect_position_time": 0,
120      "mistake_time_spent": 0,
121      "has_ai_model": true,
122      "exercise_type": "rep"
123    }
124  ]
125}

Session fields:


FieldTypeDescription
idintSession id
created_at / updated_attimestampWhen the record was saved / last updated (ISO 8601)
company_user_idstringThe user's id — same value as x-user-id
company_idintInternal id of your company
integration_optionstringWhich integration surface launched the workout ("workout" by default)
client_session_idstring \nullSDK-generated idempotency key for the save; null on sessions from older SDKs
content_id / content_title / content_image_url / content_difficultystring \nullThe workout content that was played
planned_exercisesobject[] \nullThe workout plan as launched — per exercise: exercise_id (string), title, thumbnail_url, reps_target, time_target_seconds, exercise_type ("rep" \"timer" \"none"), language
started_at / completed_attimestamp \nullSession start / completion time
actual_duration_seconds / target_duration_secondsintReal wall-clock time spent vs. planned duration, in seconds
completed_reps_count / target_reps_countintTotal reps done vs. planned across rep-based exercises
calories_burnedfloatActual calories burned
completion_percentagefloat0–100, can be fractional
accuracy_score / efficiency_scoreint0–100
total_mistakesintForm mistakes across all exercises
total_exercisesintPlanned exercise count (includes untouched exercises)
actual_hold_time_seconds / target_hold_time_secondsintTime held in correct position vs. target, for timer-based exercises
readinessobject \nullFree-form pre-workout readiness answers, when collected
post_workout_feedbackobject \nullFree-form post-workout feedback, when collected
has_rep_based_exercises / has_ai_modelboolWhether the session contained rep-based exercises / AI-tracked exercises
ai_summarystringAI-generated session summary — present only when one has been generated; omitted otherwise
exercisesobject[]Per-exercise results (see below)

Exercise entry fields (`exercises[]`):


FieldTypeDescription
idintEntry id
workout_session_idintParent session id
exercise_indexint0-based position in the workout
exercise_idint \nullExercise library id (numeric here; planned_exercises[].exercise_id is a string)
reps_doneintReps completed
reps_targetint \nullnull for timer-based exercises
time_spent_secondsintActive time on this exercise
time_target_secondsint \nullnull for rep-based exercises
caloriesfloatCalories burned on this exercise
average_accuracyint0–100; 0 when AI tracking was off
rep_accuracyint[] \nullPer-rep accuracy scores (rep-based, AI-tracked exercises)
mistakesobject[] \null[{ "mistake": "straighten back", "count": 2 }]
mistake_countintTotal mistake occurrences
perfect_position_time / mistake_time_spentintSeconds in correct / incorrect form (timer-based exercises)
has_ai_modelboolAI form tracking was active for this exercise
exercise_typestring"rep" \"timer" \"none"

Key rules:

  • Rep vs. timer: for "rep" exercises read reps_done / reps_target and rep_accuracy; for "timer" exercises read time_spent_seconds / time_target_seconds and perfect_position_time.
  • planned_exercises is what was planned, exercises is what actually happened — an abandoned session has fewer exercises[] entries than planned_exercises[]. Match them by order (exercise_index).
  • Sessions are user-scoped: requesting a session id that belongs to a different user returns 403.
  • The response may include additional internal fields not listed here — ignore unrecognized fields.
  • Errors: 400 non-numeric {id} · 401 invalid x-api-key · 403 session belongs to a different user · 404 unknown session id, or x-user-id not found for your company.

Alternatively, to open a full session summary in your app without building a page, pass the session id to the SDK's custom component view with route session/{id}. The complete card-building walkthrough (Swift + Kotlin) is in Displaying completed workouts.