# Start a session

**POST** `/api/embed/v1/sessions`

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.

Embed · Self-hosted with an Embed license

## Parameters

### Body

- `code` · Required · string: The login code, `grl_…`. The code is the credential.
- `parentOrigin` · Required · string: The 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.
- `turnOff` · Optional · array of strings: Switches to turn off for this session. Unknown IDs are listed in `ignored`.
- `replaces` · Optional · string: The `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](https://docs.gorunner.io/embed/api/sign-in/renew-session), or with a new code and `replaces`. `switches` holds every switch’s value for it; `me` is who the frame is.

## Responses

- 201 Created

  The session.

- 400 `VALIDATION_ERROR`

  `parentOrigin` or another field is missing or not valid. The code isn’t used up.

- 401 `INVALID_CODE`

  The code is unknown, used or past its 60 seconds.

- 401 `EMBED_SESSION_ENDED`

  The code’s person no longer has that access (`details.reason`).

- 403 `EMBED_NOT_AVAILABLE`

  Embed isn’t licensed here, or sign-in is off (`details.reason`: `not_licensed` or `sign_in_off`). The code isn’t used up.

- 403 `ORIGIN_NOT_ALLOWED`

  The page isn’t an allowed website for this account.

- 409 `ACCOUNT_DELETED`

  The account is deleted.

- 500 `INTERNAL_ERROR`

  Something failed on the installation’s side. Try again.

Any call can also answer: [`413 PAYLOAD_TOO_LARGE`](https://docs.gorunner.io/errors#PAYLOAD_TOO_LARGE) · [`415 INVALID_CONTENT_TYPE`](https://docs.gorunner.io/errors#INVALID_CONTENT_TYPE) · [`429 RATE_LIMITED`](https://docs.gorunner.io/errors#RATE_LIMITED) · [`503 BUSY`](https://docs.gorunner.io/errors#BUSY). See [Errors](https://docs.gorunner.io/embed/api#errors).

Request

**curl**

```bash
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"
  }'
```

**JavaScript**

```js
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();
```

**Python**

```python
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

```json
{
  "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

- `code` · Required · string
- `parentOrigin` · Required · string: The host page’s origin. Missing or empty is 400 `VALIDATION_ERROR` (`details.field` `parentOrigin`) and the login code is NOT used, so a retry with it works.
- `turnOff` · Optional · array of strings
- `replaces` · Optional · string: The `gri_…` token this one renews.

### Response · 201 Created

- `data` · object
  - `token` · string: `gri_…` — send as `Authorization: Bearer`; keep in memory only.
  - `expiresAt` · time
  - `renewAfter` · time
  - `renewableUntil` · time: When renewing in the frame stops for this line of sessions (12 hours after the login code, the `EMBED_SESSION_MAX_HOURS` setting). Absent for a platform preview.
  - `renewInSeconds` · integer: `renewAfter` as 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.
  - `account` · object
    - `id` · string
    - `status` · string
  - `workspace` · object
    - `id` · string or null: The partner’s workspace id; null for the default workspace.
    - `default` · boolean
  - `user` · object
    - `id` · string
  - `role` · string: One of `viewer`, `operator`, `editor`.
  - `switches` · object: Every switch id with its effective value; a missing id is off.
    - `{key}` · boolean: Each key’s value.
  - `ignored` · array of strings
  - `me` · object: What `GET /api/auth/me` answers this session.

Related: [Make a login code](https://docs.gorunner.io/embed/api/sign-in/issue-login-code) · [Renew a session](https://docs.gorunner.io/embed/api/sign-in/renew-session) · [Login codes and sessions](https://docs.gorunner.io/embed/sign-in-and-sessions)

Source: https://docs.gorunner.io/embed/api/sign-in/start-session
