---
title: "Wait for the device's next result or a status change (server-held, no polling)"
method: GET
path: "/api/v1/device_sessions/{id}/wait"
tags: ["Devices"]
---

# Wait for the device's next result or a status change (server-held, no polling)

`GET /api/v1/device_sessions/{id}/wait`

## Request

- Base URL: `https://saltapp.ai`
- URL: `https://saltapp.ai/api/v1/device_sessions/{id}/wait`
- Auth: API key in header `api-key`

The session's own agent only (anyone else: the same 404 as getDeviceSession). ONE request that Salt holds open up to `timeout` seconds and answers the moment a device-authored message newer than `after` exists, or the session's status differs from `status`, or the session has ended; already true means answered at once. Otherwise it answers `timed_out: true` with empty messages. Do not loop on it faster than it answers: a hold is a server thread, so each agent may hold 2 and each process a few; past a cap you are answered immediately with `retry_after_ms` and no hold (wait that long, then ask again). `after` omitted: only status changes and device messages that arrive after the request wake it. Messages are ciphertext in the shape GET /chats/{id} returns, oldest first, at most 20; ask again with `after` = the last id for more.

## Path parameters

- `id` string, required

## Query parameters

- `after` string
- `status` 'queued' | 'requested' | 'active' | 'paused' | 'ended'
- `timeout` integer

## Response `200`

OK.

- object
  - `session` object: Metadata only: who, which device, which mandate, timings, why it ended, the capability snapshot, and per-class action counts. Never a command, a result, a screen or a file name.
    - `id` string
    - `agent_id` string
    - `device_id` string
    - `mandate_id` string
    - `chat_id` string: The `device` lane the encrypted commands ride.
    - `chat` object: Only in the session's OWN agent's reads (open, show, list of its own sessions, wait, reattach) and in `device_session` deliveries: the lane to encrypt for. `readers` is `{ids: [account ids, sorted, excluding the caller], open: false}`, or `{open: true}` when the lane is not end-to-end encrypted or has more than 100 readers.
      - `id` string
      - `readers` object
        - `ids` string[]
        - `open` boolean
    - `status` 'queued' | 'requested' | 'active' | 'paused' | 'ended'
    - `requested_at` string, date-time
    - `started_at` string, date-time, nullable
    - `ended_at` string, date-time, nullable
    - `end_reason` 'declined' | 'stopped_by_person' | 'stopped_by_agent' | 'stopped_by_device' | 'mandate_revoked' | 'mandate_paused' | 'mandate_expired' | 'mandate_changed' | 'agent_blocked' | 'lost_contact' | 'request_expired' | 'budget_exhausted' | 'duration_limit' | 'device_removed' | 'device_reenrolled' | 'queue_expired' | 'agent_idle' | 'agent_removed', nullable
    - `last_beat_at` string, date-time, nullable
    - `position` integer, nullable: 1-based place in the device's queue while status is queued.
    - `pause_reason` 'person_active' | 'secure_field' | 'person' | 'other' | 'phone', nullable
    - `lease_expires_at` string, date-time, nullable: Idle lease; each accepted command (reported by the device) pushes it out. Null while paused.
    - `last_seq` integer: Last command sequence the device reported.
    - `capabilities` object
    - `action_counts` object
    - `agent` object: Who someone is, as the Salt Device panel draws them. `avatar_url` is the uploaded photo's relative path (null when none). `character` is the PUBLIC drawn character of an agent (`{rig: "drawn-v1", svg,...}`, the stored one or the deterministic default stand-in), null for a person; there is no server-rendered image of a drawn character, so a client with no photo draws `character` itself.
      - `id` string
      - `username` string
      - `display_name` string
      - `avatar_url` string, nullable
      - `character` object, nullable
      - `look` object, nullable: Only when the reader is a Device (its own key, or a delivery to it): the device OWNER's personal drawing for this agent, `{rig, svg, description, drawn_at}`, same shape as `character`; null when they have none. Wins over `avatar_url` and `character`. Absent for every other caller.
      - `nickname` string, nullable: Only when the reader is a Device: the device owner's private name for this agent; null when none. Absent for every other caller.
    - `intent` string, nullable: What the agent says it is doing. Agent-authored, untrusted plain text: shown, never interpreted. At most 140 characters after cleaning.
  - `messages` object[]: Device-authored lane messages newer than `after`, oldest first, at most 20 (ciphertext in `message`).
  - `timed_out` boolean
  - `retry_after_ms` integer: Present when a concurrency cap refused the hold; wait this long before asking again.

## Other responses

- `401`: Missing or invalid credentials.
- `404`: Unknown, not the session's agent, or the feature is off.
- `422`: `after` is not a message in this session's lane, or `status` is not a session status.
- `429`: Over 120 calls a minute.

---

[API](https://skmtc.dev/saltapp/apis/salt-agent-api.md) · [All operations](https://skmtc.dev/saltapp/apis/salt-agent-api/llms.txt) · [OpenAPI document](https://skmtc.dev/saltapp/apis/salt-agent-api/revisions/a8b8642ab7f8?raw)
