YouTube’s service integrity system requires proof-of-origin tokens (PoTokens) for certain web and TV clients to verify that requests originate from a legitimate browser or device context. Inner includes a pure-KotlinDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/faraasaaay/inner/llms.txt
Use this file to discover all available pages before exploring further.
PoTokenGenerator that produces deterministic tokens from a visitor or account identifier — no WebView, BotGuard JavaScript execution, or native code required.
When PoTokens are needed
Service integrity enforcement is applied to web and TV clients. Inner’sPlaybackAuthState.needsServiceIntegrity(client) returns true for the following clientName values:
| Client | Needs service integrity |
|---|---|
WEB | ✅ |
WEB_REMIX | ✅ |
WEB_CREATOR | ✅ |
MWEB | ✅ |
WEB_EMBEDDED_PLAYER | ✅ |
TVHTML5 | ✅ |
TVHTML5_SIMPLY_EMBEDDED_PLAYER | ✅ |
TVHTML5_SIMPLY | ✅ |
IOS / IOS_MUSIC | ❌ |
ANDROID / ANDROID_MUSIC | ❌ |
ANDROID_VR | ❌ |
IOS, IOS_MUSIC, MOBILE, or ANDROID_MUSIC for playback, you can leave webClientPoTokenEnabled = false and skip token generation entirely.
Token types
poToken — generic fallback
poToken — generic fallback
Stored in
PlaybackAuthState.poToken. Used as a fallback when neither poTokenPlayer nor poTokenGvs is set. Both resolvePlayerPoToken() and resolveGvsPoToken() fall back to this value.poTokenPlayer — player request token
poTokenPlayer — player request token
Stored in
PlaybackAuthState.poTokenPlayer. Placed in the serviceIntegrityDimensions.poToken field of player request bodies when the target client needsServiceIntegrity. This is the token YouTube uses to verify the player call itself.poTokenGvs — GVS stream token
poTokenGvs — GVS stream token
Stored in
PlaybackAuthState.poTokenGvs. Appended as the pot query parameter to Google Video Server (GVS) stream URLs: https://rr*.googlevideo.com/…?pot=<token>. YouTube’s media servers use this to validate stream access.Generating tokens
PoTokenGenerator exposes three methods. generateSessionToken and generateContentToken are convenience wrappers that call generateColdStartToken with a specific clientState value:
dataSyncId instead of visitorData as the identifier when the user is authenticated (YouTube.hasLoginCookie() == true), since dataSyncId is the authenticated session identifier. You can also use PlaybackAuthState.sessionId, which automatically returns dataSyncId when available and falls back to visitorData.
Applying tokens
Once you have generated tokens, configure them on theYouTube object. Setting webClientPoTokenEnabled = true is required — without it all token resolution is suppressed regardless of which token fields are set.
How Inner uses tokens
Player token resolution
When
YouTube.player(videoId, playlistId, client) is called, it invokes resolvePlayerPoToken(client, explicitPoToken, authState).The resolution logic in PlaybackAuthState:- If an explicit
poTokenargument was passed toplayer(), use it directly. - If
webClientPoTokenEnabledisfalse, returnnull. - If
needsServiceIntegrity(client)isfalse, returnnull. - Otherwise return
poTokenPlayer ?: poToken.
serviceIntegrityDimensions.poToken field of the player request body.GVS stream token resolution
When
YouTube.registerPlayback(...) is called — which hits the playback tracking endpoint after stream selection — the GVS token is resolved via resolveGvsPoToken(authState):- If a
clientargument is provided andneedsServiceIntegrity(client)isfalse, returnnull. - If
webClientPoTokenEnabledisfalse, returnnull. - Otherwise return
poTokenGvs ?: poToken.
Stream URL decoration
The
appendGvsPoToken(url, client, authState) helper appends ?pot=<token> (or &pot=<token>) to a stream URL. It only appends if the URL does not already contain pot= and if resolveGvsPoToken returns a non-null value. This is called when constructing the final media URL passed to the player.Token internals
The token structure follows SmartTube’sPoTokenService approach. PoTokenGenerator uses four constants:
| Constant | Value | Role |
|---|---|---|
MAGIC_HEADER | 0x0A | Opens the outer payload; protobuf field tag for the random key |
TOKEN_VERSION | 0x22 | Field tag separating the key from the encrypted identifier |
INNER_TAG | 0x38 | Opens the inner payload; field tag for the client-state string |
TIMESTAMP_TAG | 0x02 | Field tag for the timestamp within the inner payload |
- 16-byte random key — generated fresh for each token via
Random.nextBytes(16). - XOR-encrypted identifier — the UTF-8 bytes of
visitorData(ordataSyncId) are XOR-encrypted byte-by-byte against the random key, cycling the key for identifiers longer than 16 bytes. - Inner payload —
INNER_TAG+ varint length +clientStatebytes +TIMESTAMP_TAG+ varint length + little-endian timestamp bytes (current milliseconds since epoch, with trailing zero bytes stripped). - Outer payload —
MAGIC_HEADER+ varint(16) + key bytes +TOKEN_VERSION+ varint(encryptedId.size) + encrypted identifier bytes + inner payload. - Encoding — the full outer payload is base64url-encoded without padding (
=characters are trimmed).