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

# Quickstart

> Register a developer app, authorize a user, and send your first playback event.

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](https://punchplay.tv/developers)
* 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](https://punchplay.tv/developers), 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:

```text theme={null}
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:

```bash theme={null}
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. Apps without callbacks: device code

Request a device code:

```bash theme={null}
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`.

The example requests only playback scopes. A full native client normally requests the scope set documented in the [Native App API](/native-app-api#recommended-scope-set).

Example response:

```json theme={null}
{
  "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:

```bash theme={null}
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:

```json theme={null}
{ "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:

```json theme={null}
{
  "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

```bash theme={null}
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:

```json theme={null}
{
  "ok": true,
  "media_type": "movie",
  "tmdb_id": 550,
  "tvdb_id": null,
  "imdb_id": "tt0137523",
  "punchplay_id": "tmdb:movie:550"
}
```

## 5. Read now-playing state

```bash theme={null}
curl https://punchplay.tv/api/platform/v1/playback/now-playing \
  -H "Authorization: Bearer ACCESS_TOKEN"
```

Example response:

```json theme={null}
{
  "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:

```bash theme={null}
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"
```

Successful refresh returns a rotated refresh token. Replace the old one immediately.

```json theme={null}
{
  "access_token": "new_access_token",
  "refresh_token": "new_refresh_token",
  "token_type": "bearer",
  "expires_in": 2592000,
  "refresh_expires_in": 31415926,
  "scope": "profile:read playback:read playback:write"
}
```

Device-code clients can refresh here:

```bash theme={null}
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"
  }'
```

Successful refresh returns a rotated refresh token. Replace the old one immediately.

```json theme={null}
{
  "access_token": "new_access_token",
  "refresh_token": "new_refresh_token",
  "token_type": "bearer",
  "expires_in": 2592000,
  "refresh_expires_in": 31415926,
  "scope": "profile:read playback:read playback:write events:read"
}
```

## 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](/authentication) for both auth flows, token lifecycle, and security guidance.
* Read [Native App API](/native-app-api) for title, history, ratings, lists, collection, calendar, and Continue Watching endpoints.
* Read [Playback API](/playback-api) for payload design, event ordering, and multi-episode behavior.
* Read [Live Events](/live-events) to subscribe to playback changes in real time.
