2026-10-13
Added
- History rows from
/me/history, and history and playback rows from/me/sync/snapshotand/me/sync/changes, now includesourceSeasonandsourceEpisode: the episode in TMDB numbering. They differ fromseasonandepisodeonly for an anime episode stored in TVDB order, and arenullfor movies. See Anime episode numbering /playback/in-progressitems now documentsourceSeasonandsourceEpisode, which they already returned
Fixed
/playback/in-progressno longer hides an item because the user watched that title before. Only a watch logged at or after the item’s last playback event clears it, so a rewatch stays resumable
2026-10-12
Added
- History results from
POST /api/platform/v1/sync/historynow includestored: the media source, source id, season and episode the row was saved under. Anime episodes are kept in TVDB order, so these can differ from the numbers sent. See Anime episode numbering - History rows from
/me/history,/me/sync/snapshotand/me/sync/changesnow includeclientItemId, theclient_item_idyour app sent when it wrote the row, ornullfor rows written any other way
2026-10-05
Changed
- Refreshing with the previous refresh token within 60 seconds of exchanging it now returns a new token pair instead of
invalid_grant, so a lost refresh response no longer forces the user to reconnect. The pair from the lost response is no longer valid; only the newest pair is - Presenting a refresh token pair that such a retry replaced now revokes the connection, as presenting the previous token after the 60 seconds already did
- Two concurrent refreshes with the same refresh token now both succeed instead of one failing with
invalid_grant; only the later pair stays valid
2026-09-29
Added
- Continue Watching summaries now document fields they already returned:
currentSeasonNumber,currentSeasonName,currentSeasonEpisodesWatched,currentSeasonEpisodesTotal,currentSeasonPercent,currentSeasonMinutesRemaining,episodeRuntimeSource, andepisodeBackdropUrl - Continue Watching summaries now include
earlierUnwatchedSourceSeason,earlierUnwatchedSourceEpisode, andearlierUnwatchedEpisodeTitlefor linking to the earlier gap - Continue Watching summaries now include
currentSeasonEpisodesUnaired, the announced episodes of the current season that have not aired yet
Changed
earlierUnwatchedSeasonandearlierUnwatchedEpisodenow point at the earliest gap in a season the user has started before falling back to seasons they never started.earlierUnwatchedCountstill counts every aired unwatched episode before the last-watched onecurrentSeasonMinutesRemainingnow sums each remaining episode’s listed runtime instead of multiplying by an average
Fixed
GET /api/platform/v1/title/{type}/{id}no longer includes the user’s private per-title note ininteraction. The field was never documented;interactionnow contains exactlywatchedAt,rating,isFavourite, andshowStatus
2026-09-22
Added
- Continue Watching summaries now include
earlierUnwatchedSeason,earlierUnwatchedEpisode, andearlierUnwatchedCount, allowing clients to surface aired gaps separately from the forward “next episode” action - Continue Watching summaries now include
releasePhase, a schedule-based display state separate from the existing providerstatus
Fixed
nextSeasonandnextEpisodenow advance from the last-watched episode instead of rewinding to an earlier unwatched gap, and continue to exclude unaired episodes- Fully caught-up shows remain in Continue Watching when their next episode is announced, even when it does not yet have an air date
- Continue Watching and recent-history surfaces replace identifier-shaped stored titles such as
tmdb:124101with canonical catalog names when available; show years use the canonical premiere year
Updated
nextEpisodeToAir.airDateis explicitly optional so clients can represent announced but unscheduled episodes
2026-09-19
Fixed
- Setting a show’s
showStatustoDROPPEDnow removes it from Continue Watching, Up Next, and upcoming/calendar recommendations without deleting watch history - Site and Platform API drop/undrop actions now update the same state, including canonical TMDB/TVDB aliases; setting another status or
nullrestores the show
Updated
- Ratings, favourites, and watch-status library endpoints now honor
pageSize(capped at 100) - Watch-status reads now return all title-level statuses by default and can be filtered explicitly with
status=DROPPED
2026-09-05
Documentation updates
- Corrected quickstart scopes and playback examples.
- Fixed Kotlin authentication and sync examples.
- Clarified pagination, rate limits, live events, and playback progress.
- Updated the OpenAPI reference and SDK installation guidance.
2026-08-09
Fixed
- An incomplete
POST /api/platform/v1/playback/stopnow creates inactive Continue Watching progress without requiring a preceding live playback event - A later passive resume snapshot can update the same stable
playback_session_idinstead of being silently deduped after the first stop - Resume snapshots are ordered by provider
event_created_atrather than database write time, preventing delayed older snapshots from replacing newer progress - Unexpected playback processing failures now return the standard error envelope and request ID and are retained in platform request logs
- Collection creation no longer returns a successful response with a null resource ID after a uniqueness race
Updated
- Playback payloads now reject unknown fields and invalid ranges.
progressis a ratio from0to1and can deriveposition_secondswhenduration_secondsis supplied - Playback failures now distinguish
400 invalid_request,409 playback_state_conflict,422 media_not_found, and500 server_error - The Platform API contract is now
1.0.0-beta.4
Breaking
- Playback writes now require
event_id,event_created_at,media_type,title, andplayback_session_id. Episode writes also requireseasonandepisode - A standalone
pausenow returns409 playback_state_conflictinstead of reporting success without creating visible progress
Clarified
- Passive resume imports should send one incomplete
stop;progressfollowed bypauseis reserved for live playback state
2026-07-29
Security
- Access tokens now expire after one hour instead of 30 days, for both the OAuth and device-code flows. They’re bearer credentials with no proof of possession and travel widely — addon config files, mobile storage, proxy logs, crash reports — so a short lifetime bounds the exposure window from leaks that go unnoticed. Refresh promptly on
401, or shortly beforeexpires_inelapses - Refresh tokens already rotated on use; a consumed one is now recognised as replayed rather than merely invalid. Replaying a refresh token revokes its entire chain, since a legitimate client already exchanged it
Fixed
- The change feed could permanently skip a change. Ids are assigned when a change is written but only become visible when its transaction commits, so two changes committing out of order could leave one behind an advanced cursor, silently desynchronising a mirror. Cursors now also record which transactions were open when issued, and re-check them on the next read
Updated
- Change-feed delivery is now explicitly at-least-once. Apply changes idempotently, keyed on
resourceandresourceId— receiving one twice is normal. Existing cursors keep working and upgrade automatically on their next read
2026-07-28
Added
- Bulk writes at
POST /api/platform/v1/sync/history,/sync/ratings, and/sync/watchlist, accepting up to 100 items and 512 KiB per request - A durable change feed at
GET /api/platform/v1/me/sync/changesand per-resource backfill atGET /api/platform/v1/me/sync/snapshot, so large clients mirror an account instead of re-polling library endpoints. Cursors stay valid for 90 days - Required
Idempotency-Keyon every bulk write, with results replayed for 24 hours - Token revocation at
POST /api/platform/v1/oauth/revoke(RFC 7009) - OAuth server metadata at
/.well-known/oauth-authorization-server(RFC 8414), so standard OAuth libraries can discover the endpoints X-RateLimit-Limit,X-RateLimit-Remaining, andX-RateLimit-Reseton every platform response, exposed via CORS for browser clients- Cursor pagination on
GET /api/platform/v1/me/collectionandGET /api/platform/v1/me/listsvialimitandcursor, returningnextCursor
Updated
- Every authenticated endpoint now enforces a per-token bucket (300 reads or 120 writes per minute) and a per-app bucket (6,000 per minute across all its users). Endpoint-specific limits apply on top of these
- Bulk sync accepts 512 KiB bodies rather than the general 64 KiB ceiling, so a full 100-item batch of long-titled entries is no longer rejected
- Every
429now carriesRetry-After
Fixed
/.well-known/oauth-authorization-serverreturned anissuerwith a trailing slash. RFC 8414 requires it to match the string the well-known URI was built from, and strict OAuth clients abandoned discovery on the mismatch
Breaking
GET /api/platform/v1/me/collectionandGET /api/platform/v1/me/listsnow return a bounded page (100 and 50 by default) plusnextCursor, where they previously returned the entire set. FollownextCursorto retrieve everything- A stale
Idempotency-Keywhose original request died before recording its result now returns409 idempotency_indeterminateinstead of silently re-running the write. That mutation may already have applied: reconcile through/me/sync/changesand retry with a new key
2026-07-27
Updated
- Platform errors now consistently return
error,message, andrequest_id, including delegated list, history, rating, collection, and profile failures - Platform JSON and form bodies are capped at 64 KiB with a 10-second read timeout; the OpenAPI contract now documents
408and413responses - OAuth and device-flow status contracts now match production, including HTTP
400forauthorization_pending,401for invalid clients, and explicit302authorization redirects - List and collection writes now derive anime classification from canonical TVDB identity when clients omit the optional compatibility hint
2026-07-26
Added
- Stable anime search, trending, and discovery through the public catalog API
- Explicit anime classification on collection items while preserving their routable movie/show kind
Updated
- Anime titles use TMDB base identities with TVDB enrichment and official episode ordering for mapped series
- Playback, list, collection, SDK, and OpenAPI contracts now distinguish anime category from movie/show kind
- Legacy provider-ID translation remains an internal import and redirect compatibility concern
2026-07-23
Updated
- All 16 developer-app scopes are now listed consistently across the Authentication guide, native-app guide, and OpenAPI reference
- PunchPlay’s APNs device-registration routes are enforced as first-party-only; third-party apps retain notification inbox, unread-state, and preference access
- Authenticated Platform API operations now publish concrete request and success-response schemas instead of generic JSON placeholders
- Public catalog and community rate limits are documented alongside the existing public-profile limit
2026-07-19
Added
- Stable public catalog search, trending, and discovery endpoints
- Stable public community overview and authenticated community-stat endpoints
- Native notification inbox, unread count, mark-read, and notification-preference endpoints
- First-party-only APNs push-device registration and removal endpoints
- Native trophy, profile, and user-preference endpoints
- Four least-privilege scopes:
notifications:read,notifications:write,trophies:read, andprofile:write
Updated
- Native clients can keep discovery, community, notifications, trophies, profile editing, and preferences inside the app
- Sensitive account operations such as password, email, imports, connected services, and account deletion remain web-only
- The recommended first-party mobile authorization set now contains 16 scopes
Notes
- Anime discovery was not yet included in that mobile-client release
- Third-party apps can use the notification inbox and preference APIs, but PunchPlay’s APNs device-registration routes are reserved for first-party clients
2026-07-18
Added
- Platform title, season, and episode detail endpoints for native clients
- Watch-history reads, logging, date editing, and deletion
- Ratings, favourites, planning, and watch-status reads and writes
- User list CRUD and list-item management
- Physical and digital collection reads and writes
- Calendar and Continue Watching endpoints
- Public documentation for all 12 developer-app scopes
Updated
- Native clients can now implement the core PunchPlay movie and TV experience without linking to the web application
- The OpenAPI reference now covers the complete
/api/platform/v1route tree - OAuth guidance now recommends authorization code + PKCE for native apps that can receive a callback
- The reserved
email:readscope is no longer offered to developer apps
Current boundary
- Notifications, trophies, and account settings remain web experiences
- Community endpoints are used by the first-party mobile client but are not yet part of the stable
/api/platform/v1contract - Anime discovery was not yet included in that mobile-client release
2026-06-16
Added
- Persistent platform request logging keyed by
request_idfor support and incident investigation - Open developer app registration in PunchPlay settings for confidential and public clients
Updated
- OAuth refresh responses now rotate and return a new
refresh_tokenwithrefresh_expires_in - Device refresh responses now rotate and return a new
refresh_tokenwithrefresh_expires_in - Playback and auth docs now document the current debugging and token rotation contract
- OpenAPI reference now reflects the live refresh-token response shape
2026-06-09
Added
- Authorization code + PKCE support for PunchPlay developer apps
- App-level
allowed_scopespolicy for developer app registration - Request-scoped platform error IDs via
request_idandX-PunchPlay-Request-Id - Scope enforcement updates across
/me, playback, device auth, and OAuth token flows - Connected app revocation support in user settings
Updated
- Authentication docs now cover both OAuth and device code flows
- Quickstart now reflects web, browser, native, desktop, and TV integration paths
- Error docs now describe the current debugging and support contract
- OpenAPI reference now reflects the current platform auth surface
2026-06-08
Added
- Initial PunchPlay developer portal
- Platform API documentation for device authentication
- Platform playback documentation for
start,pause,resume,stop, andprogress - Live playback guides for now-playing, in-progress, and SSE updates
- Public API documentation for public user profile data
- Generated endpoint reference from the initial OpenAPI definition
- Developer app registration with
client_idissuance - Confidential and public client support
- Scope-aware platform tokens
Notes
- The current public platform contract is
v1. - Future work is expected in areas such as app registration automation, outbound webhooks, and richer read and write surfaces.