> ## Documentation Index
> Fetch the complete documentation index at: https://docs.punchplay.tv/llms.txt
> Use this file to discover all available pages before exploring further.

# Playback API

> Send playback lifecycle events into PunchPlay and keep user state in sync.

The playback API is the core of the PunchPlay platform. It accepts playback lifecycle events and turns them into now-playing state, in-progress items, playback sessions, and watch history.

This surface requires the `playback:write` scope. A `stop` that resolves as watched and creates history also requires `history:write`.

## Supported actions

Send these actions to:

```text theme={null}
POST /api/platform/v1/playback/{action}
```

Available actions:

* `start`
* `pause`
* `resume`
* `stop`
* `progress`

## Minimum payload shape

At a minimum, send enough identity to resolve the title reliably.

```json theme={null}
{
  "media_type": "movie",
  "title": "Fight Club",
  "year": 1999,
  "tmdb_id": 550,
  "playback_session_id": "player-session-123"
}
```

## Supported payload fields

| Field                 | Type                 | Notes                                                           |
| --------------------- | -------------------- | --------------------------------------------------------------- |
| `event_id`            | string               | Stable event identifier for dedupe                              |
| `media_type`          | `movie` or `episode` | Required                                                        |
| `title`               | string               | Required in practice for reliable identification                |
| `year`                | number               | Helpful for movie disambiguation                                |
| `imdb_id`             | string               | Optional external identifier                                    |
| `tmdb_id`             | number               | Preferred metadata identifier                                   |
| `tvdb_id`             | number               | TVDB identifier used for mapped anime-series enrichment         |
| `punchplay_id`        | string               | PunchPlay-native identifier when available                      |
| `season`              | number               | Episode season number                                           |
| `episode`             | number               | Episode number                                                  |
| `episode_end`         | number               | End episode for multi-episode playback                          |
| `absolute_episode`    | number               | Absolute numbering resolved against TVDB anime episode ordering |
| `episode_title`       | string               | Optional episode title                                          |
| `multi_episode`       | boolean              | Indicates multi-episode playback                                |
| `anime`               | boolean              | Classifies playback as anime without changing `media_type`      |
| `progress`            | number               | Fractional progress between `0` and `1`                         |
| `duration_seconds`    | number               | Total runtime                                                   |
| `position_seconds`    | number               | Current playback position                                       |
| `device_id`           | string               | Client or device identity                                       |
| `playback_session_id` | string               | Critical for ordering and session lifecycle                     |
| `event_created_at`    | number               | Millisecond timestamp from the client                           |
| `client_version`      | string               | Integration version for diagnostics                             |
| `watched`             | boolean              | Explicit watch completion override on stop                      |
| `watched_threshold`   | number               | Completion threshold when using progress-derived stops          |
| `raw_filename`        | string               | Helps fallback matching                                         |

## Movie example

```bash theme={null}
curl -X POST https://punchplay.tv/api/platform/v1/playback/progress \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event_id": "evt_123",
    "media_type": "movie",
    "title": "Fight Club",
    "year": 1999,
    "tmdb_id": 550,
    "position_seconds": 1800,
    "duration_seconds": 8340,
    "progress": 0.2158,
    "device_id": "living-room-mac",
    "playback_session_id": "player-session-123",
    "event_created_at": 1780910400000
  }'
```

## Episode example

```bash theme={null}
curl -X POST https://punchplay.tv/api/platform/v1/playback/stop \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event_id": "evt_456",
    "media_type": "episode",
    "title": "Severance",
    "tmdb_id": 95396,
    "season": 1,
    "episode": 2,
    "episode_title": "Half Loop",
    "position_seconds": 3000,
    "duration_seconds": 3200,
    "watched_threshold": 0.9,
    "playback_session_id": "session-episode-456",
    "event_created_at": 1780910460000
  }'
```

## Event ordering behavior

PunchPlay applies session-aware event handling so clients can send real playback events without manually reconstructing user state.

Important behaviors:

* Duplicate `event_id` values are deduped.
* A `stop` may create a completed watch or only save progress, depending on completion state.
* Older events that arrive after a stop are ignored.
* Newer progress after a stopped session can reopen the session.
* `pause` updates state without creating a watch.

## Best practices

* Always send a stable `playback_session_id`.
* Send `event_created_at` from the client, not the server, when possible.
* Send `tmdb_id` whenever available.
* Include `position_seconds` and `duration_seconds` for accurate progress.
* Treat `event_id` as required if your client can generate one.

## Completion rules

A `stop` event is treated as watched when one of these is true:

* `watched` is explicitly `true`
* `progress` or derived progress meets `watched_threshold`

If a stop does not qualify as watched, PunchPlay stores progress instead of creating a completed watch entry.

When a stop does qualify as watched, the token must include `history:write`. Request that scope during authorization for clients that are allowed to create completed watch history.

## Failure handling

Platform playback errors include:

* `request_id` in the JSON body
* `X-PunchPlay-Request-Id` in the response headers

Capture that value when reporting failed writes or invalid payloads.
