> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.chaser.sh/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.chaser.sh/_mcp/server.

# Sessions API

All session lifecycle endpoints. Authenticated via Bearer API key.

## POST /v1/sessions

Create a new browser session.

```bash
curl -s https://api.chaser.sh/v1/sessions \
  -X POST \
  -H "Authorization: Bearer $CHASER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ttl_seconds": 1800
  }' | jq
```

**Request body:**

| Field | Type | Default | Description |
|---|---|---|---|
| `profile` | string | default | Internal profile label. Omit unless instructed. |
| `ttl_seconds` | integer | `1800` | Session lifetime in seconds. Range: 60–3600. |

**Response `201`:**

```json
{
  "id": "sess_019ec0d0-a88a-73f2-9741-8030dc82a854",
  "object": "session",
  "status": "creating",
  "cdp_url": "wss://cdp.chaser.sh/sessions/sess_019ec0d0-a88a-73f2-9741-8030dc82a854",
  "created_at": "2026-06-13T11:49:20.651182Z",
  "expires_at": "2026-06-13T11:59:20.650185Z"
}
```

The session transitions from `creating` to `ready` in ~15–25 seconds. The `cdp_url` field appears when ready.

---

## GET /v1/sessions

List sessions in the authenticated workspace, newest first. Cursor-paged.

```bash
curl -s "https://api.chaser.sh/v1/sessions?limit=10" \
  -H "Authorization: Bearer $CHASER_KEY" | jq
```

**Query parameters:**

| Parameter | Type | Default | Description |
|---|---|---|---|
| `limit` | integer | `20` | Max results per page. Range: 1–100. |
| `cursor` | string | — | Cursor from previous response's `next_cursor`. |

**Response `200`:**

```json
{
  "object": "list",
  "data": [
    {
      "id": "sess_019ec0d0-a88a-73f2-9741-8030dc82a854",
      "object": "session",
      "status": "ready",
      "device": "Android 10 - Chrome 137 (Mobile)",
      "cdp_url": "wss://cdp.chaser.sh/sessions/sess_019ec0d0-a88a-73f2-9741-8030dc82a854",
      "bytes_up": 22500,
      "bytes_down": 25503,
      "proxy_ip": null,
      "proxy_region": null,
      "created_at": "2026-06-13T11:49:20.651182Z",
      "ready_at": "2026-06-13T11:49:38.102017Z",
      "expires_at": "2026-06-13T11:59:20.650185Z",
      "ended_at": null,
      "ended_reason": null
    }
  ],
  "next_cursor": "MjAyNi0wNi0xM1QxMTo0OToyMC42NTExODIrMDA6MDB8MDE5ZWMwZDAt...",
  "has_more": true
}
```

---

## GET /v1/sessions/:id

Get a single session by ID.

```bash
curl -s "https://api.chaser.sh/v1/sessions/sess_019ec0b4-b174-7e90-91b3-02032470cb24" \
  -H "Authorization: Bearer $CHASER_KEY" | jq
```

**Response `200`:**

```json
{
  "id": "sess_019ec0d0-a88a-73f2-9741-8030dc82a854",
  "object": "session",
  "status": "ready",
  "device": "Android 10 - Chrome 137 (Mobile)",
  "cdp_url": "wss://cdp.chaser.sh/sessions/sess_019ec0d0-a88a-73f2-9741-8030dc82a854",
  "bytes_up": 22500,
  "bytes_down": 25503,
  "proxy_ip": null,
  "proxy_region": null,
  "created_at": "2026-06-13T11:49:20.651182Z",
  "ready_at": "2026-06-13T11:49:38.102017Z",
  "expires_at": "2026-06-13T11:59:20.650185Z",
  "ended_at": null,
  "ended_reason": null
}
```

| Field | Description |
|---|---|
| `status` | `creating`, `ready`, `closed`, or `error` |
| `cdp_url` | WebSocket CDP endpoint. Present from creation. |
| `device` | Human-readable device description. |
| `bytes_up` | Total bytes uploaded (cumulative). |
| `bytes_down` | Total bytes downloaded (cumulative). |
| `proxy_ip` | Egress proxy IP. Populated once connected. |
| `proxy_region` | Egress proxy region. |
| `ready_at` | When the session became ready. |
| `ended_at` | When the session ended (if closed). |
| `ended_reason` | Why the session ended (if closed). |

**Errors:**

| Status | Code | Meaning |
|---|---|---|
| 404 | `session_not_found` | Session doesn't exist or doesn't belong to your workspace. |

---

## DELETE /v1/sessions/:id

Delete a session. Stops egress, releases the proxy, marks `ended_reason: "client_delete"`.

```bash
curl -s -X DELETE "https://api.chaser.sh/v1/sessions/sess_019ec0b4-b174-7e90-91b3-02032470cb24" \
  -H "Authorization: Bearer $CHASER_KEY" \
  -w "\nHTTP %{http_code}\n"
```

**Response:** `204` (no body).

**Errors:**

| Status | Code | Meaning |
|---|---|---|
| 409 | `session_not_open` | Session is already closed/expired. That's fine — it's already stopped. |

---

## POST /v1/sessions/:id/screenshot

Capture the session framebuffer as a PNG.

```bash
curl -s "https://api.chaser.sh/v1/sessions/sess_019ec0d0-a88a-73f2-9741-8030dc82a854/screenshot" \
  -X POST \
  -H "Authorization: Bearer $CHASER_KEY" \
  -o screenshot.png \
  -w "HTTP %{http_code}, size: %{size_download} bytes\n"
```

**Response:** Raw PNG bytes (`image/png`). No JSON wrapping.

**Errors:**

| Status | Code | Meaning |
|---|---|---|
| 409 | `session_not_open` | Session is not in `ready` state. |
| 404 | `session_not_found` | Session doesn't exist or doesn't belong to your workspace. |

---

## POST /v1/sessions/:id/preview-token

Mint a token for the live-view WebSocket stream.

```bash
curl -s "https://api.chaser.sh/v1/sessions/sess_019ec0d0-a88a-73f2-9741-8030dc82a854/preview-token" \
  -X POST \
  -H "Authorization: Bearer $CHASER_KEY" \
  -H "Content-Type: application/json" \
  -d '{}' | jq
```

**Request body:** Empty JSON object `{}`. Reserved for future options.

**Response `200`:**

```json
{
  "url": "wss://stream.chaser.sh/sessions/sess_019ec0d0-a88a-73f2-9741-8030dc82a854/preview?token=eyJ...",
  "expires_at": "2026-06-13T11:54:41.836Z",
  "input_enabled": true,
  "fps": 5
}
```

| Field | Description |
|---|---|
| `url` | WebSocket URL for the preview stream. Open in the dashboard. |
| `expires_at` | Token validity. Mint a new token to resume. |
| `input_enabled` | Whether click/tap input is forwarded to the session. |
| `fps` | Frame rate (currently 5). |

**Errors:**

| Status | Code | Meaning |
|---|---|---|
| 409 | `session_not_open` | Session is not in `ready` state. |
| 404 | `session_not_found` | Session doesn't exist or doesn't belong to your workspace. |