This is the same logical session throughout — no extra concurrency slot is
taken. Each refresh reserves a fresh cycle of session-minutes (metered
fail-closed), so a handoff costs a new minute-cycle, not a new session.
The double-buffer
1
Mint & connect — session A
Mint a session token on your server
(
POST /v1/sessions/token) and connect it in the browser
(POST /v1/sessions/connect). This is your visible stream, session A.2
When A drifts, refresh for a fresh runtime token
Call The response is
POST /v1/sessions/refresh with the same session token. It mints a
brand-new runtime connect token for the same logical session:{ reactorJwt, expiresIn, expiresAt } — a fresh runtime
token, nothing else.3
Boot a warm session B, seeded from A's last frame
Open a hidden second player with the fresh
reactorJwt, seeding it from
the last frame of session A so the two streams line up. Let it warm for a
couple of seconds until it’s streaming cleanly.4
Crossfade, then drop A
At the next idle gap (never mid-action) crossfade the visible layer from A to
B, then unmount A. B is now your live stream — repeat from step 2 the next
time it drifts.
Metering & status codes
Each refresh reserves one fresh cycle ofsession_seconds against your daily
budget before it mints the token, so continuity is metered honestly.
On a
402, don’t tear down the visible stream — the drifted-but-live picture
is better than a dead one. Just stop refreshing until the budget resets.Works for worlds and characters
/v1/sessions/refresh is world-agnostic: it authorizes by the play token’s
app + session id and mints a fresh runtime token regardless of what’s playing.
The same handoff powers the first-party character embeds (which re-seed to
hold a mood without a mid-speech cut) and world embeds alike, so you can use
this pattern for either.
Next steps
Run a Studio world
Play a world you built visually through the session flow.
Embedding
Theming, events, and the built-in token refresh callback.

