Skip to content

Start device sign-in

POST
/api/v1/device-codes
curl --request POST \
--url https://example.com/api/v1/device-codes \
--header 'Content-Type: application/json' \
--data '{ "name": "example", "kind": "browser", "clientId": "example" }'

The device shows code and linkUrl (with a QR code of linkUrl), and polls with secret every interval seconds until a signed-in user approves or denies the code. The code is 6 characters, works once and expires after 5 minutes; create another for a new one. Too many codes pending from one address, or one IPv6 /64, is 429 with code throttled.

Media typeapplication/json
object
name
required

The device’s own name, such as “Living Room” or “Firefox on Linux”, shown before approving and as its session’s name.

string
>= 1 characters <= 100 characters
kind

Default app. A browser link signs in the browser that started it, by cookie.

string
Allowed values: browser app
clientId

The app’s own random ID for itself, as at POST /api/v1/sessions; the approved session replaces the approving user’s session on that client. A browser sends none; its ID is the server’s client cookie.

string
>= 1 characters <= 100 characters

The code is waiting for approval.

Media typeapplication/json

A device code waiting for approval.

object
code
required

Six characters to show, from A to Z without I and O, and 2 to 9.

string
secret
required

What the device polls with. Shown only here; never show it.

string
expiresAt
required
string format: date-time
interval
required

Seconds between polls.

integer
linkUrl
required

The web client’s /link page with code and the server’s ID (server) filled in, at the address the device reached the server by; for the QR code. It holds no secret.

string

Example generated

{
"code": "example",
"secret": "example",
"expiresAt": "2026-04-15T12:00:00Z",
"interval": 1,
"linkUrl": "example"
}

The request is malformed or a field is invalid (code invalidRequest).

Media typeapplication/problem+json

An RFC 9457 problem document. code is stable and is what clients translate.

object
type
required

A URI naming the problem type; about:blank when only code matters.

string
title
required

A short English summary, for logs.

string
status
required
integer
detail

English detail for logs and developers; never internal error text.

string
instance
string
code
required

A problem’s stable code, which clients translate. A client that doesn’t know a code goes by the response’s status.

string
Allowed values: invalidRequest unauthenticated invalidCredentials wrongPassword forbidden crossOrigin denied notFound expired notReady rangeNotSatisfiable conflict alreadySetUp nameTaken lastAdmin ownUser fileStillPresent kindMismatch notStoppable streamLimit notPlayable noFallback playEnded throttled rateLimited providerUnavailable notConfigured quotaUsed transcodeBusy transcodeFailed internal
requestId
required

The request ID, also in the X-Request-Id header and the server log.

string
reason

For transcodeBusy and notPlayable, why. For transcodeBusy: transcodeLimit or storageFull. For notPlayable: noTracks, fileUnplayable, subtitleUnplayable, transcodeFailed, keyframeIndexMissing or downloadsNotAllowed.

string
Allowed values: noTracks fileUnplayable subtitleUnplayable keyframeIndexMissing transcodeFailed transcodeLimit storageFull downloadsNotAllowed sourceUnreadable subtitlesUnavailable planUnreadable
mismatch

For fileUnplayable, the capability the file failed.

object
field
required
string
declared

The declared value, lists joined by commas; absent when the client declared none.

string
actual
required
string
retryAfterSeconds

For throttled and rateLimited, the Retry-After header’s value.

integer
errors

For invalidRequest, what’s wrong with which field.

Array<object>
object
field
required

A JSON pointer into the body, or a parameter name.

string
code
required

What’s wrong with a field: tooLarge for a file or list over its size, unsupported for a file in a format the server doesn’t take, unknownEpisodeGroup for a group:<ID> ordering TMDb doesn’t list for the series, and notABook for a work edited with books that isn’t one.

string
Allowed values: required invalid tooShort tooLong tooLarge unsupported outOfRange unknownEpisodeGroup notABook
plays

For streamLimit, the user’s plays, to show what is playing where.

Array<object>

One of the user’s plays, for the stream limit.

object
id
required

A UUIDv7.

string format: uuid
workId
required

A UUIDv7.

string format: uuid
title
required
string
clientName

The name of the session or API key it was started by.

string
startedAt
required
string format: date-time
message

For playEnded, the admin’s words to the viewer, when they gave any.

string
resetsAt

For quotaUsed, when the quota resets, when the provider said.

string format: date-time

Example

{
"code": "invalidRequest",
"reason": "noTracks",
"errors": [
{
"code": "required"
}
]
}

Sign-in throttled (code throttled), the server too busy checking passwords or with device codes to take another, or too many client error reports (code rateLimited).

Media typeapplication/problem+json

An RFC 9457 problem document. code is stable and is what clients translate.

object
type
required

A URI naming the problem type; about:blank when only code matters.

string
title
required

A short English summary, for logs.

string
status
required
integer
detail

English detail for logs and developers; never internal error text.

string
instance
string
code
required

A problem’s stable code, which clients translate. A client that doesn’t know a code goes by the response’s status.

string
Allowed values: invalidRequest unauthenticated invalidCredentials wrongPassword forbidden crossOrigin denied notFound expired notReady rangeNotSatisfiable conflict alreadySetUp nameTaken lastAdmin ownUser fileStillPresent kindMismatch notStoppable streamLimit notPlayable noFallback playEnded throttled rateLimited providerUnavailable notConfigured quotaUsed transcodeBusy transcodeFailed internal
requestId
required

The request ID, also in the X-Request-Id header and the server log.

string
reason

For transcodeBusy and notPlayable, why. For transcodeBusy: transcodeLimit or storageFull. For notPlayable: noTracks, fileUnplayable, subtitleUnplayable, transcodeFailed, keyframeIndexMissing or downloadsNotAllowed.

string
Allowed values: noTracks fileUnplayable subtitleUnplayable keyframeIndexMissing transcodeFailed transcodeLimit storageFull downloadsNotAllowed sourceUnreadable subtitlesUnavailable planUnreadable
mismatch

For fileUnplayable, the capability the file failed.

object
field
required
string
declared

The declared value, lists joined by commas; absent when the client declared none.

string
actual
required
string
retryAfterSeconds

For throttled and rateLimited, the Retry-After header’s value.

integer
errors

For invalidRequest, what’s wrong with which field.

Array<object>
object
field
required

A JSON pointer into the body, or a parameter name.

string
code
required

What’s wrong with a field: tooLarge for a file or list over its size, unsupported for a file in a format the server doesn’t take, unknownEpisodeGroup for a group:<ID> ordering TMDb doesn’t list for the series, and notABook for a work edited with books that isn’t one.

string
Allowed values: required invalid tooShort tooLong tooLarge unsupported outOfRange unknownEpisodeGroup notABook
plays

For streamLimit, the user’s plays, to show what is playing where.

Array<object>

One of the user’s plays, for the stream limit.

object
id
required

A UUIDv7.

string format: uuid
workId
required

A UUIDv7.

string format: uuid
title
required
string
clientName

The name of the session or API key it was started by.

string
startedAt
required
string format: date-time
message

For playEnded, the admin’s words to the viewer, when they gave any.

string
resetsAt

For quotaUsed, when the quota resets, when the provider said.

string format: date-time

Example

{
"code": "invalidRequest",
"reason": "noTracks",
"errors": [
{
"code": "required"
}
]
}
Retry-After
integer

Seconds to wait.

A server-side failure: 500 (code internal, or transcodeFailed for a play’s media), or 503 for something unavailable (codes providerUnavailable and transcodeBusy, for now; notConfigured, until an admin configures it).

Media typeapplication/problem+json

An RFC 9457 problem document. code is stable and is what clients translate.

object
type
required

A URI naming the problem type; about:blank when only code matters.

string
title
required

A short English summary, for logs.

string
status
required
integer
detail

English detail for logs and developers; never internal error text.

string
instance
string
code
required

A problem’s stable code, which clients translate. A client that doesn’t know a code goes by the response’s status.

string
Allowed values: invalidRequest unauthenticated invalidCredentials wrongPassword forbidden crossOrigin denied notFound expired notReady rangeNotSatisfiable conflict alreadySetUp nameTaken lastAdmin ownUser fileStillPresent kindMismatch notStoppable streamLimit notPlayable noFallback playEnded throttled rateLimited providerUnavailable notConfigured quotaUsed transcodeBusy transcodeFailed internal
requestId
required

The request ID, also in the X-Request-Id header and the server log.

string
reason

For transcodeBusy and notPlayable, why. For transcodeBusy: transcodeLimit or storageFull. For notPlayable: noTracks, fileUnplayable, subtitleUnplayable, transcodeFailed, keyframeIndexMissing or downloadsNotAllowed.

string
Allowed values: noTracks fileUnplayable subtitleUnplayable keyframeIndexMissing transcodeFailed transcodeLimit storageFull downloadsNotAllowed sourceUnreadable subtitlesUnavailable planUnreadable
mismatch

For fileUnplayable, the capability the file failed.

object
field
required
string
declared

The declared value, lists joined by commas; absent when the client declared none.

string
actual
required
string
retryAfterSeconds

For throttled and rateLimited, the Retry-After header’s value.

integer
errors

For invalidRequest, what’s wrong with which field.

Array<object>
object
field
required

A JSON pointer into the body, or a parameter name.

string
code
required

What’s wrong with a field: tooLarge for a file or list over its size, unsupported for a file in a format the server doesn’t take, unknownEpisodeGroup for a group:<ID> ordering TMDb doesn’t list for the series, and notABook for a work edited with books that isn’t one.

string
Allowed values: required invalid tooShort tooLong tooLarge unsupported outOfRange unknownEpisodeGroup notABook
plays

For streamLimit, the user’s plays, to show what is playing where.

Array<object>

One of the user’s plays, for the stream limit.

object
id
required

A UUIDv7.

string format: uuid
workId
required

A UUIDv7.

string format: uuid
title
required
string
clientName

The name of the session or API key it was started by.

string
startedAt
required
string format: date-time
message

For playEnded, the admin’s words to the viewer, when they gave any.

string
resetsAt

For quotaUsed, when the quota resets, when the provider said.

string format: date-time

Example

{
"code": "invalidRequest",
"reason": "noTracks",
"errors": [
{
"code": "required"
}
]
}