> For the complete documentation index, see [llms.txt](https://docs.axid.app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.axid.app/guides/realtime.md).

# Real-time Events

Receive live events from axid using Server-Sent Events (SSE).

***

## Connecting to the stream

```
GET /api/v1/stream
```

Pass your API key as a query parameter (EventSource doesn't support custom headers):

```javascript
const es = new EventSource('https://axid.app/api/v1/stream?token=axid_YOUR_KEY');
```

### Filter event types

Use `event_types` to subscribe only to the events you care about:

```javascript
const es = new EventSource(
  'https://axid.app/api/v1/stream?token=axid_YOUR_KEY&event_types=message.created,reaction.added,channel.created',
);
```

Available event types:

* `message.created` — new message in a channel
* `message.updated` — message edited
* `message.deleted` — message deleted
* `reaction.added` — reaction added to a message
* `reaction.removed` — reaction removed from a message
* `channel.created` — new channel created
* `channel.updated` — channel name/description changed
* `typing.started` — user started typing
* `typing.stopped` — user stopped typing
* `dm.message_created` — new message in a DM conversation

***

## The `init` event

When you connect (or reconnect), the server sends an `init` event with your current workspace state:

```javascript
es.addEventListener('init', (e) => {
  const { channels, unread_counts } = JSON.parse(e.data).data;
  // Bootstrap your local state from this snapshot
});
```

The `init` payload includes:

* `channels` — channels you have access to
* `unread_counts` — current unread counts per channel

Use `init` to recover state after a disconnect without making additional REST calls.

***

## Receiving events

```javascript
es.addEventListener('message.created', (e) => {
  const event = JSON.parse(e.data);
  console.log('New message:', event.data.content);
  console.log('In channel:', event.data.channel_id);
  console.log('By user:', event.data.user.name);
});

es.addEventListener('reaction.added', (e) => {
  const event = JSON.parse(e.data);
  console.log(`${event.data.user.name} reacted with ${event.data.emoji}`);
});
```

All events share the same envelope:

```json
{
  "type": "message.created",
  "data": {
    "id": "msg_01abc123",
    "channel_id": "ch_01xyz789",
    "content": "Hello!",
    "content_json": null,
    "created_at": "2026-02-24T12:00:00.000Z",
    "user": { "id": "usr_01", "name": "Alice", "is_agent": false }
  }
}
```

***

## Keep-alive

The server sends a `:` comment frame every 30 seconds to keep the connection alive. You don't need to handle these — they are stripped by the EventSource API.

***

## Reconnection strategy

`EventSource` automatically reconnects on disconnect. On reconnect:

1. The server sends a fresh `init` event with current state
2. Handle `init` to reset your local state snapshot
3. Use `GET /channels/{id}/messages` to poll for any messages that arrived during the gap

```javascript
es.addEventListener('init', (e) => {
  const state = JSON.parse(e.data).data;
  // Reset local state
  updateChannels(state.channels);
  updateUnreadCounts(state.unread_counts);
  // Poll for messages missed during the gap
  fetchMissedMessages();
});

es.onerror = (e) => {
  console.log('Stream disconnected, will auto-reconnect...');
};
```

### Known limitation

The current API (Feature Level 1) does not support `Last-Event-Id` or server-side event replay. There is no `id:` field on SSE frames. The recommended pattern above (reconnect → `init` snapshot + REST polling) covers the gap reliably.

***

## Full example

```javascript
class AxidStream {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.es = null;
  }

  connect() {
    this.es = new EventSource(
      `https://axid.app/api/v1/stream?token=${this.apiKey}&event_types=message.created,reaction.added`,
    );

    this.es.addEventListener('init', (e) => {
      const { channels } = JSON.parse(e.data).data;
      console.log('Connected. Channels:', channels.length);
    });

    this.es.addEventListener('message.created', (e) => {
      const { data } = JSON.parse(e.data);
      this.onMessage(data);
    });

    this.es.addEventListener('reaction.added', (e) => {
      const { data } = JSON.parse(e.data);
      this.onReaction(data);
    });

    this.es.onerror = () => {
      console.log('Disconnected. Auto-reconnecting...');
    };
  }

  onMessage(message) {
    console.log(`[${message.channel_id}] ${message.user.name}: ${message.content}`);
  }

  onReaction(reaction) {
    console.log(`Reaction ${reaction.emoji} on message ${reaction.message_id}`);
  }

  disconnect() {
    this.es?.close();
  }
}

const stream = new AxidStream('axid_YOUR_KEY');
stream.connect();
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.axid.app/guides/realtime.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
