---
audience: developer
summary: Presence batch fetch + room state snapshot.
title: Presence and state API
path: reference/api/presence-and-state
status: published
---

# Presence and state API

3 endpoints.

| Method | Path | Purpose |
|---|---|---|
| `GET` | `/v1/presence` | Batch-fetch presence for a list of participant ids. |
| `GET` | `/v1/rooms/{room_id}/state` | Snapshot of a room's authoritative state (members, engagement, settings, plan). |
| `PUT` | `/v1/typing` | Send your typing indicator. |

## GET /v1/presence

```bash
curl "$BASE/v1/presence?ids=p1,p2,p3" \
  -H "Authorization: Bearer $TOKEN"
```

```json
{
  "data": {
    "p1": "online",
    "p2": "idle",
    "p3": "offline"
  }
}
```

Use this for member lists and search results. Don't poll — subscribe
to the `presence` WebSocket event for live updates and use this for
the initial snapshot.

States: `online`, `idle`, `busy`, `appear_offline`, `offline`.

## GET /v1/rooms/{room_id}/state

A single denormalised snapshot of everything client-side state
typically needs:

```jsonc
{
  "data": {
    "room": { /* room metadata */ },
    "members": [ /* members with their per-room state */ ],
    "engagement": { /* mode + per-AI overrides */ },
    "plan": null,         // or full plan if one is active
    "active_sidekicks": [ /* live sidekicks */ ],
    "active_call": null,  // or call state
    "unread_count": 7,
    "last_read_event_id": "evt-…",
    "muted_until": null,
    "snoozed_event_ids": []
  }
}
```

Faster than 7 parallel requests on first room-open. Internal use is
the client mounting a fresh room view; you can use it too.

## PUT /v1/typing

```bash
curl -X PUT "$BASE/v1/typing" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"room_id": "room-…", "typing": true}'
```

Sets your typing indicator. Auto-clears after 4 seconds of no
re-publish. Use `typing: false` to clear early.

Fan out to other room members via the `typing` WebSocket event.
