> For the complete documentation index, see [llms.txt](https://posetracker.gitbook.io/posetracker-api/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://posetracker.gitbook.io/posetracker-api/use-posetracker-on-real-time-camera-webcam/tracking-endpoint-message-to-handle.md).

# Tracking Endpoint Message to handle

The **same message structure** is used across:

* real-time tracking
* upload tracking (video/image)

***

### Form score and grades (V3)

Each completed repetition is scored **0–100** and mapped to a grade:

* **A**: score ≥ 90
* **B**: score ≥ 80
* **C**: score ≥ 70
* **D**: score ≥ 60
* **F**: score < 60

The grade reflects execution quality (depth, angles, tempo, stability, and form).

#### Where the grade is sent

In V3, the authoritative score/grade for a **counted rep** is sent inside the **`counter` event** (`counter.form_score`).

> Do not rely on a separate `form_score` event to get the grade of the rep that was just counted.

***

### Message types (V3)

#### Core lifecycle

* `initialization`
* `posture`
* `counter`
* `error`

#### Optional outputs (plan-gated)

* `keypoints`
* `angles`
* `progression`
* `recommendations`

#### Jump-specific (custom exercises)

* `jump_calibration`, `jump_started`, `jump_discarded`, `jump_height`, `jump_summary`

***

### `counter` (updated V3 payload)

The `counter` message is sent **each time the rep count changes**. For upload video, a final counter is emitted once processing ends (`final: true`).

#### Fields

* `type`: `"counter"`
* `current_count`: `number` — total counted reps so far
* `final`: `boolean` (optional) — true when processing has finished (upload video)
* `form_score`: `object` (optional) — score for the rep that was just counted (or current state when `final:true`)
  * `score`: `number` (0–100)
  * `avg_score`: `number` (running average)
  * `grade`: `"A" | "B" | "C" | "D" | "F"`

#### `reference_score` (when using `reference`)

When using `reference=REFERENCE_UUID`, the `counter` event may include a `reference_score` object.

This object contains similarity metrics between the user’s movement and the selected reference movement.

* `overallScore` (`number`, `0..1`) — overall similarity score
* `poseScore` (`number`, `0..1`) — pose similarity
* `timingScore` (`number`, `0..1`) — timing similarity
* `movementScore` (`number`, `0..1`) — movement amplitude similarity
* `grade` (`string`) — letter grade for the repetition

Example:

```json
{
  "type": "counter",
  "current_count": 3,
  "reference_score": {
    "overallScore": 0.82,
    "poseScore": 0.78,
    "timingScore": 0.9,
    "movementScore": 0.84,
    "grade": "B"
  }
}
```

Use `overallScore` if you want a single global score. Use the sub-scores if you want custom logic or custom UI feedback.

#### Concrete examples

```json
{"type":"counter","current_count":1,"form_score":{"score":88,"avg_score":88,"grade":"B"}}
```

```json
{"type":"counter","current_count":5,"form_score":{"score":72,"avg_score":81,"grade":"C"}}
```

```json
{"type":"counter","current_count":10,"final":true,"form_score":{"score":90,"avg_score":84,"grade":"A"}}
```

***

### Standalone `form_score` event (if you see it)

If a `form_score`-only message exists in other contexts (e.g. live feedback while a rep is in progress), it **must not** be treated as the authoritative grade for a **counted** rep.

**Single source of truth for counted reps:** `counter.form_score`.

***

### Tips for developers: minGrade vs client-side filtering

#### Server-side filtering (recommended): `minGrade`

If you set `minGrade=B`, PoseTracker only increments `current_count` for reps graded **A or B**. This means:

* `current_count` becomes your “valid reps” count
* each counter event already corresponds to a valid rep

#### Client-side filtering (if you don’t want server filtering)

If you do **not** set `minGrade`, you receive every counted rep with its grade inside `counter.form_score`. You can maintain your own strict count:

Pseudo-logic:

* if `payload.form_score.grade` is in `["A","B"]`, increment your local strict counter

***

### Errors

Use `error` events for both hard failures and runtime warnings.

Some errors stop the session.

Some only report degraded performance.

For load order and ready signals, see [Initialization](/posetracker-api/use-posetracker-on-real-time-camera-webcam/tracking-endpoint-message-to-handle/initialization.md).

#### Common request and access errors

You should still handle standard request errors on this route:

* invalid or missing params, including unknown exercises
* token or access errors
* developer feature restrictions on unsupported plans

#### Device capability & performance

For real-time webcam tracking, PoseTracker may fall back to slower backends.

This happens when GPU acceleration is unavailable.

{% hint style="warning" %}
These payloads use `type: "error"`, but they are performance warnings.

Tracking can continue in degraded mode.

Do not treat them as session-ending failures unless `ready: true` never arrives.
{% endhint %}

These messages are specific to real-time webcam sessions.

| `error`                 | When it is sent                                                    | Exact `message`                                                                                                                                                                                  |
| ----------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `webgl_unavailable`     | WebGL is unavailable and PoseTracker switches to another GPU path. | `WebGL is not available on this device. Switching to WebGPU backend. This device may not be well suited for real-time pose tracking.`                                                            |
| `backend_fallback_wasm` | GPU backends are unavailable and PoseTracker switches to WASM.     | `WebGL and WebGPU are not available. Switching to WASM backend. Warning: this backend significantly slows down pose estimation. This device may not be well suited for real-time pose tracking.` |
| `backend_fallback_cpu`  | GPU and WASM are unavailable and PoseTracker switches to CPU.      | `WebGL, WebGPU and WASM are not available. Switching to CPU backend. Warning: this backend is very slow. This device is not recommended for real-time pose tracking.`                            |

Example — `webgl_unavailable`:

```json
{
  "type": "error",
  "error": "webgl_unavailable",
  "message": "WebGL is not available on this device. Switching to WebGPU backend. This device may not be well suited for real-time pose tracking."
}
```

Example — `backend_fallback_wasm`:

```json
{
  "type": "error",
  "error": "backend_fallback_wasm",
  "message": "WebGL and WebGPU are not available. Switching to WASM backend. Warning: this backend significantly slows down pose estimation. This device may not be well suited for real-time pose tracking."
}
```

Example — `backend_fallback_cpu`:

```json
{
  "type": "error",
  "error": "backend_fallback_cpu",
  "message": "WebGL, WebGPU and WASM are not available. Switching to CPU backend. Warning: this backend is very slow. This device is not recommended for real-time pose tracking."
}
```

#### If all backends fail

If every backend fails, the final model failure may stay inside the WebView.

The host app may not receive one last bridge event.

Use the [Initialization](/posetracker-api/use-posetracker-on-real-time-camera-webcam/tracking-endpoint-message-to-handle/initialization.md) ready signal as the source of truth.
