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 for your app
  3. Get an access token
  4. Send your first playback event
  5. Read now-playing state
  6. Refresh the access token when needed

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 apps and websites
  • device code for TV, desktop, CLI, and native-style clients

3A. Web and browser apps: authorization code + PKCE

Redirect the user to:
https://punchplay.tv/api/platform/v1/oauth/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=https%3A%2F%2Fexample.com%2Fauth%2Fpunchplay%2Fcallback&code_challenge=YOUR_PKCE_CHALLENGE&code_challenge_method=S256&scope=profile%3Aread%20playback%3Aread%20playback%3Awrite&state=YOUR_STATE
After PunchPlay redirects back with code, exchange it for tokens:
curl -X POST https://punchplay.tv/api/platform/v1/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "code=AUTHORIZATION_CODE" \
  -d "redirect_uri=https://example.com/auth/punchplay/callback" \
  -d "code_verifier=YOUR_ORIGINAL_PKCE_VERIFIER"

3B. Native, desktop, and TV apps: device code

Request a device code:
curl -X POST https://punchplay.tv/api/platform/v1/auth/device/code \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "scope": "profile:read playback:read playback:write events:read"
  }'
Public clients send client_id only. Confidential clients send both client_id and client_secret. Example response:
{
  "user_code": "ABCD-1234",
  "device_code": "raw_device_code",
  "verification_uri": "https://punchplay.tv/link",
  "verification_uri_complete": "https://punchplay.tv/link?code=ABCD-1234&type=platform",
  "verification_uri_qr": "data:image/png;base64,...",
  "expires_in": 600,
  "scope": "profile:read playback:read playback:write events:read"
}
Display the user_code or open verification_uri_complete for the user, then poll:
curl -X POST https://punchplay.tv/api/platform/v1/auth/device/token \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "device_code": "raw_device_code",
    "device_name": "PunchPlay Companion"
  }'
While approval is pending, the API returns:
{ "error": "authorization_pending", "message": "Device code has not been approved yet.", "request_id": "ppreq_..." }
Once approved, both auth flows produce bearer credentials shaped like this:
{
  "access_token": "access_token",
  "refresh_token": "refresh_token",
  "token_type": "bearer",
  "expires_in": 2592000,
  "refresh_expires_in": 31536000,
  "scope": "profile:read playback:read playback:write"
}

4. Send your first playback event

curl -X POST https://punchplay.tv/api/platform/v1/playback/start \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "media_type": "movie",
    "title": "Fight Club",
    "year": 1999,
    "tmdb_id": 550,
    "position_seconds": 12,
    "duration_seconds": 8340,
    "playback_session_id": "player-session-123",
    "device_id": "macbook-air-living-room"
  }'
Expected response:
{
  "ok": true,
  "media_type": "movie",
  "tmdb_id": 550,
  "tvdb_id": null,
  "imdb_id": "tt0137523",
  "punchplay_id": "tmdb:movie:550"
}

5. Read now-playing state

curl https://punchplay.tv/api/platform/v1/playback/now-playing \
  -H "Authorization: Bearer ACCESS_TOKEN"
Example response:
{
  "type": "movie",
  "tmdbId": 550,
  "title": "Fight Club",
  "year": 1999,
  "progressPercent": 0.14,
  "progressSeconds": 12,
  "durationSeconds": 8340,
  "updatedAt": "2026-06-08T12:00:00.000Z",
  "nowPlaying": true,
  "playbackState": "watching",
  "mediaSource": "tmdb"
}

6. Refresh the access token when needed

Web and browser apps can refresh through the OAuth token endpoint:
curl -X POST https://punchplay.tv/api/platform/v1/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "refresh_token=REFRESH_TOKEN"
Device-code clients can refresh here:
curl -X POST https://punchplay.tv/api/platform/v1/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{
    "client_id":"YOUR_CLIENT_ID",
    "client_secret":"YOUR_CLIENT_SECRET",
    "refresh_token":"REFRESH_TOKEN"
  }'

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.

Next steps

  • Read Authentication for both auth flows, token lifecycle, and security guidance.
  • Read Playback API for payload design, event ordering, and multi-episode behavior.
  • Read Live Events to subscribe to playback changes in real time.