Start a session
This page needs your own installation with an Embed license.
POST/
Swaps a login code for a session. The frame calls it; you rarely do.
Needs no key: the login code in the body is the credential.
Parameters
Body
codeRequired · stringThe login code,
grl_…. The code is the credential.parentOriginRequired · stringThe page around the frame. It must be an allowed website, or one your server allowed for this code’s account. Missing: refused, and the code isn’t used up.
turnOffOptional · array of stringsSwitches to turn off for this session. Unknown IDs are listed in
ignored.replacesOptional · stringThe
gri_…session this one renews. It ends only if it is the same person’s.
The session
token (gri_…) is sent as Authorization: Bearer. Keep it in memory, never in a cookie. It lasts 30 minutes.
Renew it at renewAfter (renewInSeconds counts from the server’s clock) with Renew a session, or with a new code and replaces. switches holds every switch’s value for it; me is who the frame is.
Responses
- 201Created
The session.
parentOriginor another field is missing or not valid. The code isn’t used up.- 401
INVALID_CODEThe code is unknown, used or past its 60 seconds.
The code’s person no longer has that access (
details.reason).Embed isn’t licensed here, or sign-in is off (
details.reason:not_licensedorsign_in_off). The code isn’t used up.The page isn’t an allowed website for this account.
The account is deleted.
Something failed on the installation’s side. Try again.
Any call can also answer: 413 PAYLOAD_TOO_LARGE · 415 INVALID_CONTENT_TYPE · 429 RATE_LIMITED · 503 BUSY. See Errors.
Request
GORUNNER="https://automations.example.com"
curl -X POST "$GORUNNER/api/embed/v1/sessions" \
-H "Content-Type: application/json" \
-d '{
"code": "grl_…",
"parentOrigin": "https://app.bakery.example"
}'const GORUNNER = "https://automations.example.com";
const res = await fetch(
`${GORUNNER}/api/embed/v1/sessions`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
code: "grl_…",
parentOrigin: "https://app.bakery.example",
}),
},
);
const { data } = await res.json();import os, requests
GORUNNER = "https://automations.example.com"
res = requests.post(
f"{GORUNNER}/api/embed/v1/sessions",
json={
"code": "grl_…",
"parentOrigin": "https://app.bakery.example",
},
)
data = res.json()["data"]Response · 201 Created, shortened
{
"data": {
"token": "gri_…",
"expiresAt": "2026-10-25T09:30:00Z",
"renewAfter": "2026-10-25T09:25:00Z",
"renewableUntil": "2026-10-25T21:00:00Z",
"renewInSeconds": 1500,
"account": {
"id": "acct-bakery",
"status": "active"
},
"workspace": {
"id": null,
"default": true
},
"user": {
"id": "ada-123"
},
"role": "editor",
"switches": {
"overview": false,
"workflows": true,
"runs": true,
"…": true
},
"ignored": []
}
}Every field
Body
codeRequired · stringparentOriginRequired · stringThe host page’s origin. Missing or empty is 400
VALIDATION_ERROR(details.fieldparentOrigin) and the login code is NOT used, so a retry with it works.turnOffOptional · array of stringsreplacesOptional · stringThe
gri_…token this one renews.
Response · 201 Created
dataobjecttokenstringgri_…— send asAuthorization: Bearer; keep in memory only.expiresAttimerenewAftertimerenewableUntiltimeWhen renewing in the frame stops for this line of sessions (12 hours after the login code, the
EMBED_SESSION_MAX_HOURSsetting). Absent for a platform preview.renewInSecondsintegerrenewAfteras whole seconds from this answer, by the server’s clock: schedule the renewal from it, so a device whose clock is off still renews on time.accountobjectidstringstatusstringworkspaceobjectidstring or nullThe partner’s workspace id; null for the default workspace.
defaultbooleanuserobjectidstringrolestringOne of
viewer,operator,editor.switchesobjectEvery switch id with its effective value; a missing id is off.
{key}booleanEach key’s value.
ignoredarray of stringsmeobjectWhat
GET /api/auth/meanswers this session.