> ## 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.

# Data Conventions

> Field names, timestamps, identifiers, progress units, nulls, and pagination used by PunchPlay APIs.

Use the operation's generated schema as the authority. PunchPlay's Platform API
has a few deliberate naming differences between playback/bulk requests and
native library routes; converting every field with one global rule will produce
invalid requests.

## Field names

| Area                             | Examples                                                      | Convention   |
| -------------------------------- | ------------------------------------------------------------- | ------------ |
| Playback request                 | `event_id`, `event_created_at`, `position_seconds`, `tmdb_id` | `snake_case` |
| Bulk sync request                | `client_item_id`, `watched_at`, `resolved_tmdb_id`            | `snake_case` |
| Native profile/history responses | `watchedAt`, `memberSince`, `nextCursor`                      | `camelCase`  |
| Sync response                    | `resourceId`, `changedAt`, `resetRequired`                    | `camelCase`  |

The OpenAPI reference shows the exact request and response shape for each
operation. Unknown request fields are rejected by the strict playback and bulk
schemas, so copy the documented spelling.

## Time and progress

* `event_created_at` is a Unix timestamp in **milliseconds**. It describes the
  client event and should not be replaced with the retry time.
* `watchedAt`, `watched_at`, `changedAt`, and `updatedAt` are ISO 8601 date-time
  strings, normally rendered with a `Z` suffix for UTC.
* `position_seconds` and `duration_seconds` are whole seconds. Keep positions
  between zero and the duration when a duration is known.
* Playback request `progress` is a ratio from `0` to `1` (`0.5` means halfway),
  never a percentage such as `50`.
* Playback read responses expose `progressSeconds`, `durationSeconds`, and
  `progressPercent` from `0` to `100` (`50` means halfway).

## IDs, kinds, and nulls

Title paths use a routable `movie` or `show` kind. Anime is a category and may
be represented by the optional `isAnime`/`anime` classification; it does not
change a title's path kind. TMDB IDs are numeric. IMDb (`tt...`), TVDB
(`tvdb:...`), and MAL (`mal:...`) identifiers are accepted in the documented
title paths and bulk items.

`null` means the API knows that a value is absent. An omitted optional write
field means “use the operation's default” or “leave the current value
unchanged,” depending on the endpoint. Read the operation description before
using `null` to clear a value; some updates require an explicit nullable field.

## Pagination and cursors

Collection endpoints return bounded pages with an opaque `nextCursor`:

```json theme={null}
{ "items": [], "nextCursor": "opaque-value-or-null" }
```

Send the cursor back unchanged and keep every other query parameter identical.
Do not decode, sort, or construct cursor values. Page-number endpoints expose
`page`, `pageSize`, `total`, and `hasMore` instead. Public catalog and community
endpoints currently have bounded responses and do not accept `page` or `cursor`.
The sync change feed keeps a non-null checkpoint in `nextCursor` even when a page has `hasMore: false`; use that field for the next synchronization round, while a null `nextCursor` marks completion only for cursor-paginated lists.

The change feed is at least once. Apply a page idempotently, then save its
cursor in the same local transaction. A bulk write's `Idempotency-Key` is also
opaque: reuse it only for a retry of the same request body.

## Dates and localization

Treat API date-time strings as instants and format them in the user's locale at
the UI boundary. Calendar queries use the documented `YYYY-MM` month form; do
not infer a user's timezone from a browser offset when the endpoint returns an
explicit date-time.
