Sessions and Token Refresh
How long a launch token keeps a player connected, and how to hand the embed a fresh one
The embed's session runs on the launch token your backend minted. Phoenix issues no session token of its own, so the launch token is the credential for every connection the embed makes: the first one and every reconnect after a network drop, a laptop waking from sleep, or a Phoenix deployment.
What Happens When a Token Expires
| Situation | What happens |
|---|---|
The connection stays up past exp | Nothing. An open connection is not closed when the token expires |
The connection drops before exp | The embed reconnects with the same token and carries on. Players see "Reconnecting to live markets..." while it does |
The connection is down after exp (it dropped after the token expired, or the token expired while it was down) | The reconnect would be refused, so the embed stops retrying. Players see "Your session ended" and "Reload the page to continue.", and your page receives session.expired |
The player or your page switches language after exp | A language switch reconnects, so it ends the session the same way: "Your session ended" and session.expired |
Your page calls setToken with a fresh token | The embed swaps the session in place, with no reload. From the "session ended" screen it leaves that screen and reconnects |
So the lifetime you choose for launch tokens is also how long a dropped connection can recover by itself. A 10-minute token keeps leaks short, but means a player who loses the connection after 10 minutes needs your page to supply a new token.
Keep the Session Alive
Two events tell your page to mint a fresh token for the same visitor or player (same sub, same currency):
| Event | When | What to do |
|---|---|---|
token.expiring | About a minute before exp (immediately if the token is already that close), and again each time the tab becomes visible after that point | Mint a fresh token and call setToken |
session.expired | The token has expired and the embed needs to connect: the connection is down, or a language switch has to reconnect | Mint a fresh token and call setToken |
With the Host SDK:
const refresh = async () => embed.setToken(await mintCurrentSessionToken())
embed.on('token.expiring', refresh)
embed.on('session.expired', refresh)mintCurrentSessionToken is your own call to your backend. Give every fresh token its own jti if you use one (see Token Ids).
In React, keep the token in state and pass it as the token prop: a changed token is pushed to the running embed without a reload.
<PmmEmbedFrame
url="https://embed.prediction.phoenixverse.io/o/acme"
token={token}
on={{
'token.expiring': async () => setToken(await mintCurrentSessionToken()),
'session.expired': async () => setToken(await mintCurrentSessionToken()),
}}
/>setToken also upgrades a visitor to a player after sign-in (see The visitor-to-player flow). Sending the same token again does nothing.
The Reload Button
The "session ended" screen, and any failure screen where a reload can help, offers a Reload button only when your page has said it can handle a reload: reloading the iframe alone would reuse the expired token in its URL.
- The Host SDK says so for you. When the player taps Reload, your page receives
reload.requested({ reason }, which issession_endedfor an expired token andsession_failedfor any other failure); the SDK runs your listeners and then reloads your page, so the page loads with a fresh launch token. - If you use the raw bridge, include
capabilities: ['reload.requested']in yourreadymessage and reload your page when the event arrives (see Parent Bridge). - Without either, the player sees "Reload the page to continue." with no button.
A Refused Token
If setToken hands over a token Phoenix refuses (a wrong key, a missing currency, an expired token), the session ends. The embed does not stay on the player it replaced: if your page switched users, for example after a logout, the previous player's data must not stay on screen. The embed disconnects, shows the player why, and tells your page once, in the same way for every cause that stops a session:
| Cause | The player sees | Your page receives |
|---|---|---|
| The token has expired | "Your session ended" | session.expired |
| Phoenix refused the launch | A message for the cause: "This page was opened without a valid sign-in" for a token problem, "Your account cannot use prediction markets" for a blocked player, "Prediction markets are paused on this site for now" for a suspended operator, and so on | error with Phoenix's refusal code, for example invalid_signature or player_banned (see Refusals You Can Read) |
| Phoenix could not be reached | "Something went wrong. Check your connection and reload the page." | error with the code unreachable |
The way back is a fresh setToken, which works from every one of these states, or the player's Reload button where a reload can help (offered under the rule above). The embed keeps listening to your page throughout, so a failed session never blocks the next setToken.
Checklist
- Choose a token lifetime that matches how long you accept a dropped connection recovering on its own.
- Handle
token.expiringandsession.expiredwithsetToken, and log theerrorevents: a refusal code tells you why a token was refused. - Keep the same
subandcurrencywhen you refresh. - Test in sandbox: let a token expire, drop the network (for example with the browser's offline mode), restore it, and check the embed returns after your
setToken.