# Stream events

Source: https://docs.vlayers.ai/reference/events/stream-events

`GET /v1/events/stream` · scope `events:read`

Opens a Server-Sent Events stream: it replays the events after your cursor, then sends new ones as they happen. Each frame carries its cursor as `id:`; reconnect with the last one in `Last-Event-ID` and nothing is missed or repeated. A heartbeat comment arrives every 15 seconds.

The stream replays up to 7 days; an older cursor is refused with `410 cursor_expired` (page through `GET /v1/events` from it instead).

Requires an API key with the `events:read` scope.

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `after` | query | string | no | Resume after this cursor. Leave it out to start live, from now. Example: `"MDE5MmY1YjEtMGExYi03YzJk"` |
| `types` | query | string | no | Comma-separated event types, or a prefix with `.*`. Example: `"message.*"` |
| `conversationId` | query | string | no | Only events of this conversation. Example: `"0192f5b0-7c3e-7a51-9b2d-4e8f1a6c3d21"` |
| `channel` | query | string | no | Only events of this channel's messages. Example: `"sms"` |
| `last-event-id` | header | string | no | The last cursor you processed. Takes precedence over `after`; SSE clients send it for you on reconnect. Example: `"MDE5MmY1YjEtMGExYi03YzJk"` |

## Request

cURL:

```bash
curl -N "https://api.vlayers.ai/v1/events/stream?types=message.*" \
  -H "Authorization: Bearer $VOICELAYER_API_KEY" \
  -H "Accept: text/event-stream"
```

TypeScript:

```ts
import { createVoiceLayerClient, streamEvents } from '@voicelayer/api';

const client = createVoiceLayerClient({ apiKey: process.env.VOICELAYER_API_KEY! });

// Reconnects after a drop and resumes from the last cursor, so nothing is missed.
for await (const { cursor, event } of streamEvents(client, { types: ['message.*'] })) {
  console.log(event.type, event.data);
  // save `cursor` once the event is handled; pass it back as `after` to resume
}
```

Python:

```python
import os
import voicelayer
from voicelayer.events import stream_events

config = voicelayer.Configuration(access_token=os.environ["VOICELAYER_API_KEY"])

# Reconnects after a drop and resumes from the last cursor, so nothing is missed.
for item in stream_events(config, types=["message.*"]):
    print(item.event["type"], item.event["data"])
    # save item.cursor once the event is handled; pass it back as after= to resume
```

## Response `200`

```json
{
  "id": "0192f5b1-0a1b-7c2d-8e3f-4a5b6c7d8e90",
  "type": "message.received",
  "createdAt": "2026-10-20T15:04:05.000Z",
  "livemode": false,
  "data": {
    "messageId": "0192f5b0-8d1f-7b62-8c3e-5f9a2b7d4e32",
    "conversationId": "0192f5b0-7c3e-7a51-9b2d-4e8f1a6c3d21",
    "channel": "sms",
    "direction": "inbound",
    "bindingId": null,
    "status": "received",
    "text": "Do you have a table for two tonight?",
    "author": null,
    "error": null,
    "metadata": {},
    "occurredAt": "2026-10-20T15:04:05.000Z"
  }
}
```
