Skip to main content
This guide walks through the smallest useful integration:
  1. Create a developer app and get your credentials
  2. Choose the right auth flow and scopes for your app
  3. Get an access token and verify the identity
  4. Send a complete playback lifecycle
  5. Read playback state
  6. Refresh the access token when needed

Choose scopes for the job

Request only what the integration needs. These recipes assume the app is registered with the listed scopes in allowed_scopes: playback:write lets an app send lifecycle events. A stop that resolves as a completed watch also needs history:write; ask for that scope before the user authorizes the app rather than discovering the requirement after playback has started.

Before you begin

You need:
  • A PunchPlay account
  • A developer app from Get API key
  • Your app’s client_id
  • Your app’s client_secret if you created a confidential client

1. Create a developer app

Sign in to Get API key, then create either:
  • A confidential client for server-backed apps that can safely store a secret
  • A public client for native or browser-based apps that must not embed a secret
Also:
  • register your redirect URIs if you plan to use OAuth
  • choose the minimum allowed_scopes your app should ever request
  • save the secret immediately if you created a confidential client

2. Choose your auth flow

Use:
  • authorization code + PKCE for browser, website, and native apps that can receive a callback
  • device code for TV, desktop, CLI, and clients without a convenient browser callback

3A. Apps with callbacks: authorization code + PKCE

Redirect the user to:
After PunchPlay redirects back with code, exchange it for tokens:
Third-party clients use this redirect-based flow to let PunchPlay’s signed-in consent UI collect the decision. Do not call the internal consent-submission POST yourself; the API reference labels it for PunchPlay’s own same-origin UI.

3B. Apps without callbacks: device code

Request a device code:
Public clients send client_id only. Confidential clients send both client_id and client_secret. The example requests the scopes needed for a complete playback lifecycle, including a completed-watch stop. A read-only widget or a tracker that never creates completed history can use the smaller recipes above. A full native client normally requests the scope set documented in the Native App API. Example response:
Display the user_code or open verification_uri_complete for the user, then poll:
While approval is pending, the API returns:
Once approved, both auth flows produce bearer credentials shaped like this:

4. Check the authenticated identity

Before writing playback, verify that the token belongs to the account the user just authorized and that the expected scopes were granted:
The response includes the stable user ID and the granted scopes array. Stop and re-authorize if the required scope is missing.

5. Send a complete playback lifecycle

The following shell variables create fresh UUIDs and timestamps for one walkthrough. Reuse an event_id only when retrying that same logical event; generate a new one for each later event. event_created_at is Unix time in milliseconds and must describe when the client event occurred.
Start the movie:
Send progress while the user is watching:
While the session is active, read the materialized now-playing state:
An active response includes fields like these:
When the user reaches the end, send a fresh stop event. With history:write, this creates the completed watch record:
Expected response:

6. Confirm the post-stop state

After a completed stop, the item is no longer active. Read the resumable queue to confirm that no incomplete progress remains for this session:
If you query playback/now-playing after the completed stop, the response may contain no active item. Use GET /api/platform/v1/me/history with history:read when you need to verify the completed watch record.

7. Refresh the access token when needed

Web and browser apps can refresh through the OAuth token endpoint:
Successful refresh returns a rotated refresh token. Replace the old one immediately.
Device-code clients can refresh here:
Successful refresh returns a rotated refresh token. Replace the old one immediately.

Common errors

  • authorization_pending: the user has not approved the device code yet
  • invalid_scope: the request asked for a scope outside the app policy
  • invalid_client: the provided app credentials are wrong
  • insufficient_scope: the token cannot access the endpoint you called
  • 429: you are polling or sending events too aggressively
Platform failures include request_id in the body and X-PunchPlay-Request-Id in the headers. Log that value so you can send it to support.

Test safely

The current developer API does not provide a separate sandbox host. Create one PunchPlay account and one developer app dedicated to integration testing. Keep their tokens outside source control, use a known real fixture such as Fight Club (tmdb_id: 550) rather than inventing catalogue data, and clean up reversible list, collection, preference, and dismissed-progress writes in a finally path. A completed history write can award XP and playback can leave session/audit records, so run the full start-to-stop example only on that isolated account. For help, bring the UTC timestamp, endpoint, status, response body, and request ID to PunchPlay Discord.

Next steps

  • Read Authentication for both auth flows, token lifecycle, and security guidance.
  • Read Native App API for title, history, ratings, lists, collection, calendar, and Continue Watching endpoints.
  • Read Playback API for payload design, event ordering, and multi-episode behavior.
  • Read Live Events to subscribe to playback changes in real time.