# WebView messages (Tracking endpoint)

The Tracking endpoint emits structured messages during real-time tracking.

Use them to drive UI and business logic.

### How messages are delivered

In a browser iframe, messages are sent via `window.postMessage`.

In mobile apps, they go through the WebView bridge.

Some integrations stringify the payload.

Handle both cases:

* `event.data` is an object
* `event.data` is a JSON string

### Typical V3 flow (real-time)

1. `initialization` messages while the model and camera load.
2. `posture` messages until placement is good.
3. Optional placement countdown (still `posture` messages).
4. `counter` starts once tracking is active.
5. Optional advanced outputs, depending on flags:
   * `form_score`
   * `progression`
   * `recommendations`
   * `keypoints`
   * `angles`
6. Jump flows (when using `jump_analysis` / `air_time_jump`):
   * `jump_calibration` → `jump_started` → `jump_height`
   * optional `jump_discarded`

### Message types (Tracking)

#### `initialization` (realtime)

Sent while the page loads and initializes.

Fields:

* `type` (`string`) — always `"initialization"`.
* `message` (`string`) — human-readable status.
* `ready` (`boolean`) — `true` only when tracking can start.

Example:

```json
{
  "type": "initialization",
  "message": "Loading pose model",
  "ready": false
}
```

#### `error` (realtime)

Sent when the configuration or runtime fails.

Fields:

* `type` (`string`) — always `"error"`.
* `code` (`string`) — machine-readable error code.
* `message` (`string`) — human-readable description.
* `details` (`object`, optional) — extra debug info.

Example:

```json
{
  "type": "error",
  "code": "invalid_exercise",
  "message": "Unknown exercise: split",
  "details": {
    "exercise": "split"
  }
}
```

Common codes you should handle:

* `missing_token` — `token` is missing.
* `invalid_token` — token is invalid or revoked.
* `invalid_exercise` — `exercise` is missing or unknown.
* `developer_features_not_allowed` — plan-gated flag used on Free plan.
* `webgl_unavailable` — `poseBackend=webgl` requested but not available.
* `wasm_unavailable` — `poseBackend=wasm` requested but cannot load.
* `camera_permission_denied` — user denied camera access.
* `camera_not_found` — no camera available.
* `jump_analysis_missing_height` — `userHeightCm` required for `jump_analysis`.

#### `posture` (realtime placement)

Sent continuously during placement.

It tells you what the user should do to be “ready”.

Fields:

* `type` (`string`) — `"posture"`.
* `message` (`string`) — guidance text for the user.
* `direction` (`string`) — placement direction hint. Example: `"in-frame"`.
* `ready` (`boolean`) — `true` when tracking can start.
* `requirements` (`array`, optional) — unmet requirements.
* `countdownSecondsLeft` (`number`, optional) — present when countdown is enabled.

Example:

```json
{
  "type": "posture",
  "message": "Step back. Keep your full body in frame.",
  "direction": "in-frame",
  "ready": false,
  "requirements": ["full_body_visible"],
  "countdownSecondsLeft": 0
}
```

#### `counter` (realtime)

Sent when exercise tracking is active.

For repetition exercises, it increments on each validated rep.

For duration exercises, it increments over time (seconds).

Fields:

* `type` (`string`) — `"counter"`.
* `current_count` (`number`) — reps or seconds.
* `exercise` (`string`, optional) — movement key.

Example:

```json
{
  "type": "counter",
  "exercise": "squat",
  "current_count": 6
}
```

#### `form_score` (realtime, optional)

Sent when form scoring is enabled for the exercise.

This is a compact “how good is the current posture” signal.

Fields:

* `type` (`string`) — `"form_score"`.
* `score` (`number`) — `0` to `100`.
* `label` (`string`, optional) — example: `"good"`, `"needs_work"`.

Example:

```json
{
  "type": "form_score",
  "score": 82,
  "label": "good"
}
```

#### `recommendations` (realtime, plan-gated)

Sent when `recommendations=true`.

Fields:

* `type` (`string`) — `"recommendations"`.
* `data` (`array<string>`) — feedback strings, ordered by priority.

Example:

```json
{
  "type": "recommendations",
  "data": [
    "Keep your knees aligned with your toes.",
    "Lower your hips a bit more."
  ]
}
```

#### `progression` (realtime, plan-gated)

Sent when `progression=true`.

This is usually a rep-phase progress signal.

Fields:

* `type` (`string`) — `"progression"`.
* `value` (`number`) — `0` to `100`.

Example:

```json
{
  "type": "progression",
  "value": 47
}
```

#### `keypoints` (realtime, plan-gated)

Sent when `keypoints=true`.

Fields:

* `type` (`string`) — `"keypoints"`.
* `data` (`array<object>`) — keypoints.

Keypoint object:

* `name` (`string`) — landmark name.
* `x` (`number`) — pixels.
* `y` (`number`) — pixels.
* `score` (`number`) — confidence `0 → 1`.

Example:

```json
{
  "type": "keypoints",
  "data": [
    { "name": "nose", "x": 322.3, "y": 78.1, "score": 0.99 },
    { "name": "left_shoulder", "x": 366.4, "y": 132.7, "score": 0.98 }
  ]
}
```

#### `angles` (realtime, plan-gated)

Sent when `angles=true`.

Fields:

* `type` (`string`) — `"angles"`.
* `data` (`object`) — computed angles.

Example:

```json
{
  "type": "angles",
  "data": {
    "left_side": {
      "knee_angle": { "from_hip_to_ankle": 164 }
    },
    "right_side": {
      "knee_angle": { "from_hip_to_ankle": 168 }
    }
  }
}
```

#### Jump messages (realtime, custom exercises)

These fire only for:

* `exercise=jump_analysis`
* `exercise=air_time_jump`

**`jump_calibration` (jump\_analysis only)**

Sent during calibration.

```json
{
  "type": "jump_calibration",
  "ready": false,
  "message": "Hold still"
}
```

**`jump_started`**

Sent once per detected jump.

```json
{
  "type": "jump_started"
}
```

**`jump_discarded`**

Sent when a jump attempt is detected but rejected.

Typical reasons are missing hips, occlusion, or unstable baseline.

```json
{
  "type": "jump_discarded",
  "reason": "hips_not_visible"
}
```

**`jump_height`**

Sent during measurement and/or when a jump completes.

Payload differs slightly by jump type.

```json
{
  "type": "jump_height",
  "jumpHeightCm": 31.8,
  "final": true
}
```

**`jump_summary`**

Sent for upload-video analyses.

It is not typically emitted in real-time mode.

### Related

* Query params: [Query parameters (Tracking endpoint)](https://posetracker.gitbook.io/posetracker-api/use-posetracker-on-real-time-camera-webcam/query-parameters-tracking-endpoint)
* Upload messages: [WebView / native messages](https://posetracker.gitbook.io/posetracker-api/use-posetracker-on-uploaded-files/upload-tracking-endpoint-video-and-image/webview-native-messages)
