# Custom Workout Assessment

A [Custom Workout](/docs/integration/custom-workout) launched with `isAssessment: true` runs as usual and ends on fitness assessment results instead of the workout statistics: a workout score, an accuracy score, an activity level, the best exercise and the one to improve. It sends `custom_assessment_completed` with the numbers that screen shows.

**What the run sends.** Everything a Custom Workout sends stays the same: the loading events, `workout_started`, `exercise_completed` and `workout_progress` per exercise, `workout_ended`, then on the results screen `workout_overview`, `exercise_overview` and `custom_assessment_completed` in that order, the [session save events](/docs/data-points/session-upload-events) when saving is on, and `workout_completed` followed by `workout_exit_request` (`location: "statistic"`) when the user taps Continue or close. No completion overlay is shown, so there is no `workout_completion_overlay_dismissed`.

**custom_assessment_completed** is sent once per run, right after `exercise_overview`. The fields are under `data`:

| Field | Type | Description |
|-------|------|-------------|
| workout_score | number | 0 to 100: the mean of the exercise `score`s, rounded. Every exercise counts, so an unfinished or skipped one pulls it down |
| accuracy_score | number | 0 to 100: form accuracy across the run, rounded |
| activity_level | string | From `workout_score`: `"not_active"` (up to 20), `"somewhat_active"` (up to 40), `"active"` (up to 70), `"highly_active"` (above 70) |
| is_partial | boolean | `true` when at least one exercise was not completed |
| completed_exercises | number | Exercises completed (rule below) |
| planned_exercises | number | Exercises in the list (rests not counted) |
| best_exercise | object or null | `{ exercise_id, exercise_title, accuracy }`: the completed exercise with the highest accuracy |
| focus_exercise | object or null | `{ exercise_id, exercise_title, accuracy, top_mistake, mistake_count }`: the completed exercise with the lowest accuracy (ties go to the one with more mistakes). `top_mistake` is its most frequent mistake, or `null` |
| exercises | array | One entry per exercise, below |

`best_exercise` and `focus_exercise` are both `null` when fewer than two exercises were completed, or when the same exercise is both.

Each entry of `exercises`:

| Field | Type | Description |
|-------|------|-------------|
| exercise_id | string | As you sent it in `customWorkoutExercises` (always a string) |
| exercise_title | string | Exercise name |
| exercise_index | number | Its place in the workout as `exercise_completed` numbers it: 1-based, rests included |
| status | string | `"completed"`, `"unfinished"` (started, not completed) or `"skipped"` (never reached: no time and no reps) |
| score | number | 0 to 100, rounded (rules below) |
| accuracy | number or null | 0 to 100; `null` when the exercise was never reached |
| reps | number | Reps counted |
| target_reps | number or null | The `reps` you set, `null` without one |
| time_spent | number | Seconds spent |
| target_time | number or null | The `duration` you set, `null` without one |
| hold_time | number | Seconds in the correct hold position |
| end_reason | string or null | `"completed"`, `"user_exit"`, `"user_skip"`, `"auto_skip"`, or `null` |

**Completed.** An exercise the user exited, skipped or that was auto-skipped is never completed. Otherwise it is completed when it reached its `reps` target, or, without a `reps` target, when it ran at least 85% of its `duration` (without either: as soon as it was reached). Give assessment tests a `duration`: a reps-only test the user cannot finish ends by auto-skip or Next and is reported `unfinished`.

**Score per exercise** (a missing `duration` counts as 30 s):
- Rep exercises with AI tracking: reps against a target, times the mean rep accuracy: `min(1, reps / target) x accuracy x 100`. The target is your `reps`; without one, KinesteX's Squats and Chair squat use 22, Push-ups and Knee push-ups 16, and other exercises one rep per 2 s of the duration.
- Timed holds with AI tracking: seconds in the correct position over the duration.
- Without AI tracking (no model for the exercise, or motion tracking off): reps over your `reps` target, else time spent over the duration.

```json
{ "type": "custom_assessment_completed", "data": { "workout_score": 72, "accuracy_score": 88, "activity_level": "highly_active", "is_partial": false, "completed_exercises": 2, "planned_exercises": 2, "best_exercise": { "exercise_id": "101", "exercise_title": "Squats", "accuracy": 92 }, "focus_exercise": { "exercise_id": "102", "exercise_title": "Plank", "accuracy": 81, "top_mistake": "Keep your hips up", "mistake_count": 3 }, "exercises": [ { "exercise_id": "101", "exercise_title": "Squats", "exercise_index": 2, "status": "completed", "score": 82, "accuracy": 92, "reps": 10, "target_reps": 10, "time_spent": 24, "target_time": null, "hold_time": 0, "end_reason": "completed" } ] }, "timestamp": 1793368803120 }
```

**Restart.** Restart on the results screen runs the same exercise list again in place; you do not send `start` again. The order is `workout_restart` (no `workout_completed` or exit for the restarted run), then a new `workout_started` when the run starts, and a new set of end events and `custom_assessment_completed` when it ends.

---
Source: https://www.kinestex.com/docs/data-points/custom-assessment-events · Index: https://www.kinestex.com/llms.txt
