# Session lifecycle

URL: https://docs.spacerun.co/docs/platform/guides/session-lifecycle

> Start and stop a cloud-gaming session, with request fields, responses and error cases.

A session is one running game instance streamed to the user. The Platform API checks eligibility, applies the plan tier, creates the session in the Runtime service and returns what the client needs to connect. Both calls need a [user access token](https://docs.spacerun.co/docs/platform/guides/authentication) and act on the caller's own sessions.

## Start: `POST /v1/me/sessions`

Operation: [createGameSession](https://docs.spacerun.co/docs/platform/reference/game-sessions/createGameSession).

| Part | Field | Type | Notes |
| --- | --- | --- | --- |
| Header | `Idempotency-Key` | string, 8-128 chars | Required. Fresh per user action. |
| Body | `appSlug` | string, 1-100 | Required. Game slug, `[a-z0-9_-]`. |
| Body | `runtimeAppId` | string | Optional launch variant. Must be an enabled variant of the game; omitted means the default (or first) one. |
| Body | `create_lobby` | boolean | Default `false`: solo session. `true` opts in to a lobby. |
| Body | `preferences` | object | Optional; every field optional. |

`preferences` fields: `width` (320-7680), `height` (240-4320), `fps` (1-360), `bitrateKbps` (1000-150000), `hdr`, `yuv444`, `audioChannels` (1-8).

**curl**

```bash
curl -X POST https://api.example.com/v1/me/sessions \
  -H 'Authorization: Bearer <ACCESS_TOKEN>' \
  -H 'Idempotency-Key: 6f1c2b7e-example-key' \
  -H 'Content-Type: application/json' \
  -d '{
    "appSlug": "example-game",
    "create_lobby": false,
    "preferences": { "width": 2560, "height": 1440, "fps": 120, "hdr": true, "audioChannels": 6 }
  }'
```

**TypeScript**

```ts
const res = await fetch("https://api.example.com/v1/me/sessions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${accessToken}`,
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    appSlug: "example-game",
    preferences: { width: 2560, height: 1440, fps: 120 },
  }),
});
const session = await res.json(); // 201 on success
```

Response `201` (for a tier capped at 1920x1080/60 fps, HDR disabled, with locally resolved 6-channel audio):

```json
{
  "sessionId": "sess_example",
  "playUrl": "https://play.example.com/s/sess_example",
  "token": "<MEMBER_TOKEN>",
  "display": { "width": 1920, "height": 1080, "fps": 60, "hdr": false },
  "stream": {
    "width": 1920, "height": 1080, "fps": 60,
    "bitrateKbps": 12500, "hdr": false, "yuv444": false, "audioChannels": 6
  }
}
```

- `playUrl` and `token` are what the player client uses to connect to the stream. Treat `token` as a secret.
- `display` is the plan tier's display profile (advisory).
- `stream` reports locally resolved settings after tier clamping. It is not a measurement of the running stream. The current adapter forwards resolution, fps, bitrate and conditional HDR; it does not forward `yuv444` or `audioChannels`. The streaming backend may have additional capability limits.

### Tier clamping

The plan tier is a ceiling. Preferences above it are degraded, never rejected:

- Resolution keeps your aspect ratio and shrinks to fit the tier's resolution (dimensions rounded to even numbers). Sending only `width` or `height` derives the other from the tier's aspect ratio.
- `fps` is capped at the tier's fps.
- `hdr` inherits the tier setting when omitted; an explicit preference is enabled only when the tier permits it. `yuv444` defaults to false and also requires tier support, but is not forwarded by the current adapter.
- `audioChannels` inputs accept 1-8. The resolver chooses 2, 6 or 8 with a stereo minimum, so requesting 1 returns 2. The default is 2; the current adapter does not forward this value.
- `bitrateKbps` defaults to a value computed from the mode (about 12 Mbps at 1080p60) and is capped by the tier maximum.

Read `stream` to see the resolved preferences; verify actual capabilities in the player/backend rather than assuming every resolved field is enforced.

### Errors

| Status | `error` | Meaning / action |
| --- | --- | --- |
| 400 | (validation shape) | Invalid body or `Idempotency-Key`. |
| 402 | `insufficient_credits` | Account has no credits. |
| 403 | `forbidden` | Account inactive. |
| 403 | `banned` | Account banned. |
| 403 | `plan_required` | No plan that allows streaming. |
| 404 | `app_not_found` | No enabled runtime app for the game or `runtimeAppId`. |
| 409 | `concurrent_limit` | Plan's concurrent session limit reached; stop one first. |
| 409 | `idempotency_key_conflict` | Key collision; use a new key. |
| 409 | `idempotency_key_used` | That key's earlier attempt already failed or ended; use a new key. |
| 503 | `unavailable` | Runtime could not create the session; retry. |
| 503 | `panic_mode` | Maintenance; see [limits](https://docs.spacerun.co/docs/platform/guides/errors-pagination-limits). |

> **Note:**
>
> On a lost response, reuse the **same** key for the same action. An open link reuses its runtime idempotency key. A failed or ended link returns `idempotency_key_used`; start a new action with a new key. Eligibility and concurrency checks can still refuse a retry, so do not assume an identical response. Internal retries to runtime also reuse the same key. An acknowledged runtime failure marks the link failed; do not blindly retry that terminal attempt indefinitely.

## Stop: `POST /v1/me/sessions/stop`

Operation: [stopGameSessions](https://docs.spacerun.co/docs/platform/reference/game-sessions/stopGameSessions). No body. Stops all of the caller's open sessions.

```bash
curl -X POST https://api.example.com/v1/me/sessions/stop \
  -H 'Authorization: Bearer <ACCESS_TOKEN>'
```

```json
{ "stopped": true }
```

It is idempotent: with nothing open it returns `{ "stopped": false }`. A `503 unavailable` means the runtime could not stop it; retry. Stop remains available during maintenance lockdown.

## Login sessions are separate

`GET /v1/me/sessions` lists authentication sessions. Its DELETE operation revokes login access; it does not stop a running game. See [Profile and security](https://docs.spacerun.co/docs/platform/guides/profile-and-security) and use the game stop POST above when releasing a streaming session.
