---
title: "Streaming API v2: Sessions"
description: "Sign in a line, keep the session fresh, sign out and handle revocation."
---

## Sign in

```http
POST /api/v2/login
Content-Type: application/x-www-form-urlencoded

username=line1234&password=secret&device_name=LG%20webOS%2C%20Living%20room
```

`device_name` is optional (up to 64 characters). The reseller sees it in the
line's device list. When a phone signs in a TV, send the TV's name.

```json
{
  "session_token": "eyJpdi…",
  "session_id": "wSN7wDGh2fH4o6Ai1LxUAg",
  "playback_key": "AbCdEfGhIjKlMnOpQrStUv",
  "exp_user": "2027-01-15T00:00:00Z",
  "exp_token": "2026-08-13T10:00:00Z",
  "server_time": 1755079200
}
```

| Field | |
|---|---|
| `session_token` | Send it as `Authorization: Bearer <session_token>`. Opaque: do not parse it |
| `session_id` | Identifies this device. It stays the same across renewals |
| `playback_key` | base64url, 16 bytes. Builds playback tokens (section 7). Never show it |
| `exp_user` | When the line expires, or `null` if it never does |
| `exp_token` | When this session token expires (48 h at most) |
| `server_time` | The panel's clock, unix seconds (section 7.3) |

Errors: `missing_credentials`, `invalid_credentials`, `account_disabled`,
`account_expired`, `restreamer_not_allowed`, `invalid_api_key`. A line that
this key does not serve gets `invalid_credentials`, like a wrong password.

**Store `session_token`, `session_id` and `playback_key` in secure storage**
(Keychain, Android Keystore). **Never store the password**: the session token
is enough, because it renews itself for 90 days.

## Keep it fresh: `refresh-session`

```http
POST /api/v2/refresh-session
Authorization: Bearer <session_token>
```

It returns the same body as `login`. Replace the stored token and
`playback_key` (the `session_id` does not change). Call it:

- **when the app starts**, before anything else;
- **every ~15 minutes** while the app is open (it picks up package and expiry
  changes);
- **once, after a `session_expired`** from any other endpoint.

`refresh-session` accepts an **expired** session token as long as it was
issued less than **90 days** ago, so a TV that stayed off for weeks signs
itself back in. Everything is checked again: the device was not unlinked,
the line's credentials did not change, the line is active and the key is
valid.

- `401` (`session_invalid`, `session_revoked`, `credentials_changed`,
  `account_not_found`): the device is no longer linked. Delete the stored
  session and sign in or pair again.
- `403` (`account_expired`, `account_disabled`, `access_denied`, …): show
  the message and **keep the stored session**. Once the line is fixed or the
  block lifts, the next `refresh-session` works without signing in again. Do
  not retry right away: try again on the next app start or after a few
  minutes.
- No `code` (`429`, `5xx`, an HTML page): temporary. Keep the session and
  retry with backoff.

`refresh-session` itself never answers `session_expired`.

## Sign out

- `POST /api/v2/logout`: signs out **this device**. Delete the stored session
  even if the call fails.
- `POST /api/v2/logout-all`: signs out **every device** of the line.

Both answer `{"message": "…"}`.

Several devices of the same line can be signed in at once. The line's
connection limit applies to what is playing, not to sessions.

## Revocation

A session stops working when the device is signed out or unlinked by the
reseller, when the line's password or username changes, or after
`logout-all`. The API sees it within 60 seconds. The reseller can also cut
connections from the panel.
