GoRunner docs
Search or askSearch⌘K

Login codes and sessions

Your server makes a one-time login code. The embed trades it for a session and keeps that session fresh by itself.

Embed · Self-hosted with an Embed license

From your page to a session

Four parties take part. Your partner key never leaves your server, and the session never leaves the frame.

  1. 1mount() opens the frame and checks this page may show itGET /api/embed/v1/bootstrap (Your page to GoRunner)
  2. 2getCode() asks your server for a login code (Your page to Your server)
  3. 3Your server asks for a code, with your partner keyPOST /api/embed/v1/login-codes (Your server to GoRunner)
  4. 4A login code: one use, 60 secondsgrl_… (GoRunner to Your server)
  5. 5Your server hands the page the code, and only the code (Your server to Your page)
  6. 6The SDK passes the code into the frame (Your page to GoRunner frame)
  7. 7The frame trades the code for a sessionPOST /api/embed/v1/sessions (GoRunner frame to GoRunner)
  8. 8A session, held in memory for 30 minutesgri_… (GoRunner to GoRunner frame)
  9. 9Ready: mount() resolves and the customer sees GoRunner (GoRunner frame to Your page)
  1. Your page → GoRunner mount() opens the frame and checks this page may show itGET /api/embed/v1/bootstrap
  2. Your page → Your server getCode() asks your server for a login code
  3. Your server → GoRunner Your server asks for a code, with your partner keyPOST /api/embed/v1/login-codes
  4. GoRunner → Your server A login code: one use, 60 secondsgrl_…
  5. Your server → Your page Your server hands the page the code, and only the code
  6. Your page → GoRunner frame The SDK passes the code into the frame
  7. GoRunner frame → GoRunner The frame trades the code for a sessionPOST /api/embed/v1/sessions
  8. GoRunner → GoRunner frame A session, held in memory for 30 minutesgri_…
  9. GoRunner frame → Your page Ready: mount() resolves and the customer sees GoRunner

Login codes

Your server makes a code with Make a login code, naming the person, their account and their role: viewer, operator or editor. The first code for a new account or person creates them.

A code starts with grl_, works once, and runs out after 60 seconds. Never log it. One person may get 20 codes a minute.

Sessions

The frame holds its session (gri_…) in memory, never in a cookie. It lasts 30 minutes and is never extended: a renewal is a new session. Each browser tab has its own, so two tabs can be in two accounts. Only hashes of codes and sessions are kept.

Renewal

Five minutes before a session ends, the frame swaps it for a new one, and the customer notices nothing.

By itself
For 12 hours after the login code, the frame renews its own session (POST /api/embed/v1/sessions/current/renew). The new one keeps the person, account, workspace, role, every switch turned off, the key and the page. The old one ends (replaced). Your server isn’t asked.
Through your page
After the 12 hours, the SDK calls your getCode with the reason renew. So does a refused renewal: a suspended account, a Platform preview, a key revoked or past its rotation, or a website no longer allowed for the account. A tab that slept past its renewal asks too, and a reloaded frame asks with reload.
On time
Each session carries renewAfter and renewInSeconds. The frame counts the seconds, so a wrong device clock still renews on time.
Limits
At most 4 renewals a minute from one login code (EMBED_RENEW_RATE_LIMIT, Rate limits). A self-hosted installation can set the 12 hours from 1 to 24 (EMBED_SESSION_MAX_HOURS).
Older installations
Before v2026.10.4 the frame never renews by itself: it asks your page every 25 minutes.

When a session ends

The server ends a session early with a reason (details.reason). Your page never sees most reasons: the SDK asks your getCode for a new code, and the customer notices nothing. What your page sees, and the reasons behind it:

A new getCode call
With the reason renew or expired, and no event. Ended by expired, replaced, role_changed, account_suspended, account_resumed or revoked.
access_ended
One fatal error event; the embed closes for good. Ended by access_removed, account_deleted, workspace_deleted or website_removed.
licence or unavailable
One fatal error event: the license lost Embed, or the installation’s sign-in is off. Ended by not_licensed or sign_in_off.
Nothing
The frame signed itself out, on unmount() or an account switch: ended.

For what each code means for your page, see The error event.

A request with an ended session answers 401 EMBED_SESSION_ENDED, the reason in details.reason, and an open stream closes with it. not_licensed and sign_in_off answer 403 EMBED_NOT_AVAILABLE. An embed session is refused everywhere outside the embed’s own routes (403 EMBED_NOT_ALLOWED).

A suspended account is the one renewal your page hears about. Once the new session finds the account suspended, the embed sends an error event with code: "account_suspended" and fatal: false. The customer can still read everything.

Checked against 12d85b3b