> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lusor.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Follow a session

> Track a session's status, read its events and artifacts, and list past sessions.

A **session** is the record of work on a phone: its status, a step-by-step event log, a recording, and its output. Every turn creates one, and so does every [session started without an agent](https://docs.lusor.ai/agents/send-a-turn.md#sessions-without-an-agent).

## What to read

| What you want | Endpoint |
| - | - |
| The current status, cheaply | `GET /v1/sessions/{id}/status` |
| Every step: actions, screenshots, messages | `GET /v1/sessions/{id}/events` |
| The full record, with output and error | `GET /v1/sessions/{id}` |
| Screenshots, UI snapshots and output | `GET /v1/sessions/{id}/artifacts` |
| The agent's reply to a turn | `summary` of the turn in `GET /v1/agents/{id}` |
| A JSON export of the whole session | `GET /v1/sessions/{id}/breakdown` |

## Wait for the end

Poll the status every few seconds until it is final:

```bash cURL theme={null}
curl https://api.lusor.ai/v1/sessions/<session id>/status \
  -H "X-Api-Key: $LUSOR_API_KEY"
```

```python Python theme={null}
import os, time, requests

H = {"X-Api-Key": os.environ["LUSOR_API_KEY"]}
FINAL = {"completed", "complete", "failed", "cancelled"}

while True:
    s = requests.get("https://api.lusor.ai/v1/sessions/<session id>/status", headers=H).json()
    if s["status"] in FINAL or s["status"] == "awaiting_human":
        break
    time.sleep(3)
print(s["status"], s.get("error"))
```

```javascript Node.js theme={null}
const H = { "X-Api-Key": process.env.LUSOR_API_KEY };
const FINAL = new Set(["completed", "complete", "failed", "cancelled"]);

let s;
do {
  await new Promise((r) => setTimeout(r, 3000));
  s = await (await fetch("https://api.lusor.ai/v1/sessions/<session id>/status", { headers: H })).json();
} while (!FINAL.has(s.status) && s.status !== "awaiting_human");
console.log(s.status, s.error);
```

The status response has `id`, `status`, `deviceId`, `startedAt`, `completedAt` and `error`.

### Statuses

| Status | Meaning |
| - | - |
| `queued` | Accepted; waiting to start. |
| `running`, `executing` | The agent is working on the phone. |
| `awaiting_human` | The agent asked you a question and is waiting. See [Answer the agent](https://docs.lusor.ai/agents/answer-the-agent.md). |
| `completed`, `complete` | Finished successfully. |
| `failed` | Finished with an error. Read `error`. |
| `cancelled` | Stopped before it finished. |

`completed`, `complete`, `failed` and `cancelled` are final. A final session can still take a [follow-up message](https://docs.lusor.ai/agents/follow-up-messages.md).

### Failure codes

When a session fails, `error` holds a code. Two you can act on:

| `error` | Meaning | What to do |
| - | - | - |
| `secret_required:<name>` | The agent needed a password or code that it was not given. | Save it in the Vault and name it in `secrets`. See [Use saved secrets](https://docs.lusor.ai/agents/answer-the-agent.md#use-saved-secrets). |
| `credits_exhausted` | The session's model cost reached the workspace's credit balance, so it stopped. | Add credits, then send a [follow-up message](https://docs.lusor.ai/agents/follow-up-messages.md). See [Credits and minutes](https://docs.lusor.ai/billing/credits-and-minutes.md). |

## Follow events as they happen

`GET /v1/sessions/{id}/events` returns every step of the session in `data`. Each event has an `id`, a `type`, a `timestamp`, and a `data` object whose content depends on the type.

To read only new events, pass the `timestamp` of the last event you saw as `since`:

```bash theme={null}
curl "https://api.lusor.ai/v1/sessions/<session id>/events?since=2026-11-02T12:00:05.120Z" \
  -H "X-Api-Key: $LUSOR_API_KEY"
```

Each call returns only events after that time.

## Artifacts

`GET /v1/sessions/{id}/artifacts` returns the session's `output`, a list of `screenshots` (with each one's step and time), and a list of `uiStates`. To read the UI tree the agent saw at one step, request `GET /v1/sessions/{id}/ui-states/{step}.json`, for example `/ui-states/3.json`.

To view the screenshots themselves, use the [step-by-step replay](https://docs.lusor.ai/agents/recordings-and-replay.md#step-by-step-replay).

## List sessions

`GET /v1/sessions` lists your workspace's sessions, newest first. Filter it with query parameters:

| Parameter | Filters by |
| - | - |
| `deviceId` | One phone. |
| `status` | One exact status. |
| `statuses`, `statusMode` | A comma-separated list of statuses, included (`include`, the default) or excluded (`exclude`). |
| `nameContains` | Text in the session's name or prompt. |
| `createdAfter`, `createdBefore` | Creation time, as a UTC time ending in `Z`, such as `2026-11-01T00:00:00Z`. |
| `limit`, `offset` | Page size (1 to 200, default 50) and position. |

`GET /v1/workspaces/{workspaceId}/devices/{deviceId}/sessions` lists the sessions of one phone.

To give a session a display name, send `PATCH /v1/sessions/{id}` with `{ "name": "…" }`. Send `"name": null` to go back to a name taken from the prompt.

## Breakdown

`GET /v1/sessions/{id}/breakdown` returns the session and its full event list as one JSON document. It is included on Hobby plans and above. Other plans get `403` with `error: "detailed_breakdown_not_in_plan"`.

## History window

How long a session's prompt, output, events and screenshots stay readable depends on your plan. The session's `promptExpiresAt`, `outputExpiresAt`, `nameExpiresAt` and `errorExpiresAt` fields say when each one expires. See [Pricing](https://lusor.ai/pricing) for each plan's history window.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.