# Exchanging PIN for JWT Token

**URL:** <https://forums.plex.tv/t/exchanging-pin-for-jwt-token/941120>\
**Category:** Dev/API Corner\
**Tags:** pms-api\
**Created:** [July 28, 2026, 10:31pm UTC](https://forums.plex.tv/t/exchanging-pin-for-jwt-token/941120 "2026-07-28T22:31:47Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![peardox](https://avatars.discourse-cdn.com/v4/letter/p/dec6dc/32.png) [@peardox](https://forums.plex.tv/u/peardox)\
**Post date:** [July 28, 2026, 10:31pm UTC](https://forums.plex.tv/t/exchanging-pin-for-jwt-token/941120/1 "2026-07-28T22:31:47Z")

</div>

Following the docs at [https://developer.plex.tv/pms/#section/API-Info/Authenticating-with-Plex](https://developer.plex.tv/pms/#section/API-Info/Authenticating-with-Plex) I get everything going fine until I hit the last step and try exchanging my PIN for a JWT Token

Using the link page at [https://plex.tv/link/?pin=](https://plex.tv/link/?pin=)` and polling the Pin ID to find out when it’s been linked I end up with what looks like a Legacy authToken in the JSON`

`
I still wanna use the JWT so I try exchanging my PIN for a JWT via a GET https://clients.plex.tv/api/v2/pins/?deviceJWT= BUT while it says in the docs that I will get a new JWT authToken I have exactly the same authToken that was visible during the polling.
A JWT should start with eyJ but my authToken(s) don’t
Checking my Plex server with the Token shows that it appears to work (BUT it’s not a JWT!!!)`

---

<div class="post-metadata">

**Author:** ![marcelopazzo](https://sea1.discourse-cdn.com/plex/user_avatar/forums.plex.tv/marcelopazzo/32/208476_2.png) [@marcelopazzo](https://forums.plex.tv/u/marcelopazzo)\
**Post date:** [August 27, 2026, 7:45pm UTC](https://forums.plex.tv/t/exchanging-pin-for-jwt-token/941120/2 "2026-08-27T19:45:45Z")

</div>

Sorry for the slow reply, this one slipped through the cracks.

The behavior you’re describing usually means the pin poll fell back to the legacy token. That fallback is silent: if the pin wasn’t created with a `jwk`, or the `deviceJWT` on the poll is empty or can’t be matched to a registered key, the request still succeeds and `authToken` contains a legacy token instead of a JWT. No error is raised, which makes it easy to miss. That token is still valid, which is why it works against your server.

For the poll to return a proper `eyJ...` JWT, two things need to be in place:

1. **The pin is created with your device’s public key.** Include a `jwk` object in the body of `POST https://clients.plex.tv/api/v2/pins` (Ed25519/EdDSA, with a `kid`). When the user completes the link, that key gets registered to the device.
2. **The poll includes a signed device JWT.** On `GET ``https://clients.plex.tv/api/v2/pins/{id}?deviceJWT=``...`, the `deviceJWT` is signed with the private key matching the JWK from step 1, with `aud: "``plex.tv``"`, `iss: <your X-Plex-Client-Identifier>`, and the same `kid` in the JWT header. No nonce is needed in this flow.

The full flow is documented under [Authenticating with Plex](https://developer.plex.tv/pms/#section/API-Info/Authenticating-with-Plex) (Option 1), in case it helps to cross-check your requests against it. The silent fallback above isn’t called out in the docs yet, which I suspect is what made this confusing.

If you already have a working legacy token, there’s also a second path that skips pins entirely: register your JWK with `POST /api/v2/auth/jwk` (authenticated with the legacy token), then use the nonce flow (`GET /api/v2/auth/nonce`, sign a device JWT including the nonce, `POST /api/v2/auth/token`) to get the 7-day JWT and every refresh after it. That’s Option 2 on the same docs page.

If neither of these matches what you’re seeing, happy to dig further. Sharing the request/response pair from your `POST /pins` call (minus any tokens) would help narrow it down.
