---
audience: developer
summary: Long-poll endpoint for missed events. Used by clients on reconnect.
title: Sync API
path: reference/api/sync
status: published
---

# Sync API

One endpoint.

| Method | Path | Purpose |
|---|---|---|
| `GET` | `/v1/sync` | Long-poll: return events since the given stream position. |

## Query parameters

- `since` — stream position (int). Optional; default 0 (everything
  from the start, capped by `limit`).
- `limit` — max events returned. Default 100, max 500.
- `timeout` — max wait time in **ms**, default 30000 (30s). If no
  events arrive within this window, returns an empty list.

## Behaviour

Two modes:

1. **Catch-up** — if there are unread events since `since`, returns
   them immediately (up to `limit`). The client then advances its
   cursor and re-polls.
2. **Long-poll** — if there are none, the request blocks. As soon
   as a new event arrives in any room the caller can see, it
   returns. If the `timeout` elapses with nothing, returns an empty
   list (signal to re-poll).

## Example

```bash
curl "$BASE/v1/sync?since=12345&limit=100&timeout=30000" \
  -H "Authorization: Bearer $TOKEN"
```

```json
{
  "data": {
    "events": [
      {
        "id": "evt-…",
        "stream_position": 12346,
        "room_id": "room-…",
        "event_type": "swp.room.message",
        "content": { ... },
        "sender_id": "5e4d…",
        "origin_ts": 1778939467
      },
      …
    ],
    "next_cursor": 12446,
    "has_more": true
  }
}
```

`has_more` tells you whether more events are available beyond the
returned page; the client re-polls with the new cursor.

## When to use sync vs. WebSocket

Use the WebSocket stream as your primary subscription. Use
`/v1/sync` on:

- **Initial app open** — catch up since the last cursor stored locally.
- **Reconnect after WS drop** — bridge the gap.
- **Mobile or background** — when WS isn't practical to keep open.

## Stream position

Stream positions are tenant-wide monotonic integers. They don't
correspond to anything user-visible; treat them as opaque cursors.

## Permission

The endpoint only returns events for rooms you can see (membership
state in `join` or `leave`). Cross-tenant events are filtered out.
