> ## Documentation Index
> Fetch the complete documentation index at: https://meepa.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# WebSocket

> Real-time WebSocket protocol for browser and native clients

The WebSocket API provides real-time communication for chat messages, reactions, presence tracking, and typing indicators.

## Connection

**Endpoint**: `GET /api/ws`

Authentication uses the **session cookie** set at login — no token in the URL or headers is needed. The server validates the session cookie automatically on upgrade.

```javascript theme={null}
// Cookie is sent automatically by the browser
const ws = new WebSocket("wss://your-meepa-instance.example.com/api/ws");
```

Origin validation is enforced for browser clients. Non-browser clients (mobile apps, desktop, bots) that omit the `Origin` header are allowed through.

## Connection Parameters

| Parameter        | Value                                                             |
| ---------------- | ----------------------------------------------------------------- |
| Max message size | 4096 bytes                                                        |
| Ping interval    | Server sends WebSocket ping frames every 30 seconds               |
| Pong timeout     | Client must respond within 60 seconds or the connection is closed |
| Write deadline   | 10 seconds                                                        |

## Client-to-Server Events

All client messages must be JSON with this structure:

```json theme={null}
{
  "type": "event_type",
  "data": { ... }
}
```

### `ping`

Client heartbeat. The server responds with a `pong` event.

<CodeGroup>
  ```json Send theme={null}
  {
    "type": "ping"
  }
  ```

  ```json Receive theme={null}
  {
    "type": "pong"
  }
  ```
</CodeGroup>

### `subscribe`

Subscribe to one or more channels to receive their messages and events.

```json theme={null}
{
  "type": "subscribe",
  "data": {
    "channelIds": ["550e8400-e29b-41d4-a716-446655440000"]
  }
}
```

### `unsubscribe`

Unsubscribe from channels to stop receiving their events.

```json theme={null}
{
  "type": "unsubscribe",
  "data": {
    "channelIds": ["550e8400-e29b-41d4-a716-446655440000"]
  }
}
```

### `typing`

Broadcast a typing indicator to other users in a channel. The server excludes the sender from the broadcast.

```json theme={null}
{
  "type": "typing",
  "data": {
    "channelId": "550e8400-e29b-41d4-a716-446655440000"
  }
}
```

## Server-to-Client Events

All server messages follow the same JSON structure:

```json theme={null}
{
  "type": "event_type",
  "data": { ... }
}
```

### `pong`

Response to a client `ping` event.

```json theme={null}
{
  "type": "pong"
}
```

### `presence.initial`

Sent immediately after connection. Contains all currently online user IDs.

```json theme={null}
{
  "type": "presence.initial",
  "data": {
    "userIds": [
      "550e8400-e29b-41d4-a716-446655440000",
      "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
    ]
  }
}
```

### `presence.update`

Broadcast when a user goes online or offline. Sent to all connected clients.

```json theme={null}
{
  "type": "presence.update",
  "data": {
    "userId": "550e8400-e29b-41d4-a716-446655440000",
    "status": "online"
  }
}
```

The `status` field is either `"online"` or `"offline"`.

### `typing`

Broadcast when a user is typing in a channel. Only sent to other users subscribed to that channel (the sender is excluded).

```json theme={null}
{
  "type": "typing",
  "data": {
    "channelId": "550e8400-e29b-41d4-a716-446655440000",
    "userId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
  }
}
```

### `message.created`

Broadcast when a new message is posted. Only sent to users subscribed to that channel.

<CodeGroup>
  ```json Event theme={null}
  {
    "type": "message.created",
    "data": {
      "id": "msg-id",
      "channelId": "channel-id",
      "userId": "user-id",
      "content": "Hello, world!",
      "createdAt": "2026-02-14T12:34:56Z",
      "updatedAt": "2026-02-14T12:34:56Z",
      "threadId": null,
      "attachments": []
    }
  }
  ```
</CodeGroup>

### `message.updated`

Broadcast when a message is edited. Only sent to users subscribed to that channel.

<CodeGroup>
  ```json Event theme={null}
  {
    "type": "message.updated",
    "data": {
      "id": "msg-id",
      "channelId": "channel-id",
      "userId": "user-id",
      "content": "Hello, world! (edited)",
      "createdAt": "2026-02-14T12:34:56Z",
      "updatedAt": "2026-02-14T12:35:30Z",
      "threadId": null,
      "attachments": []
    }
  }
  ```
</CodeGroup>

### `message.deleted`

Broadcast when a message is deleted. Only sent to users subscribed to that channel.

```json theme={null}
{
  "type": "message.deleted",
  "data": {
    "messageId": "550e8400-e29b-41d4-a716-446655440000",
    "channelId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
  }
}
```

### `reaction.sync`

Broadcast when reactions are added or removed from a message. Contains the full reaction state for that message -- not a diff.

```json theme={null}
{
  "type": "reaction.sync",
  "data": {
    "messageId": "550e8400-e29b-41d4-a716-446655440000",
    "reactions": {
      "\ud83d\udc4d": ["6ba7b810-9dad-11d1-80b4-00c04fd430c8"],
      "\u2764\ufe0f": [
        "7c9e6679-7425-40de-944b-e07fc1f90ae7",
        "550e8400-e29b-41d4-a716-446655440000"
      ]
    }
  }
}
```

<Note>
  The `reaction.sync` event always contains the complete reaction state for the message. Replace your local reaction state entirely rather than trying to merge it.
</Note>

### `channel.created`

Broadcast when a new channel is created. Sent to all connected clients.

```json theme={null}
{
  "type": "channel.created",
  "data": {
    "id": "channel-id",
    "serverId": "server-id",
    "name": "general",
    "type": "text",
    "groupId": "group-id",
    "position": 0,
    "createdAt": "2026-02-14T12:34:56Z"
  }
}
```

### `channel.updated`

Broadcast when a channel is renamed, moved to a different group, or reordered. Sent to all connected clients.

```json theme={null}
{
  "type": "channel.updated",
  "data": {
    "id": "channel-id",
    "serverId": "server-id",
    "name": "announcements",
    "type": "text",
    "groupId": "group-id",
    "position": 1,
    "createdAt": "2026-02-14T12:34:56Z"
  }
}
```

### `channel.deleted`

Broadcast when a channel is deleted. Sent to all connected clients.

```json theme={null}
{
  "type": "channel.deleted",
  "data": {
    "channelId": "550e8400-e29b-41d4-a716-446655440000",
    "serverId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
  }
}
```

### `channel.member_added`

Broadcast to a channel when members are added. Sent to all subscribers of that channel (including the newly added members).

```json theme={null}
{
  "type": "channel.member_added",
  "data": {
    "channelId": "550e8400-e29b-41d4-a716-446655440000",
    "channel": { "id": "...", "name": "...", "serverId": "...", "type": "text" },
    "userIds": ["6ba7b810-9dad-11d1-80b4-00c04fd430c8"]
  }
}
```

### `dm.opened`

Sent directly to a user when someone opens a new DM conversation with them. Not broadcast to all clients — only the recipient receives it.

```json theme={null}
{
  "type": "dm.opened",
  "data": {
    "channel": { "id": "...", "type": "dm" },
    "user": { "id": "...", "username": "alice" }
  }
}
```

## Reconnection Strategy

Clients should implement automatic reconnection with exponential backoff:

1. On disconnect, wait 1 second before the first retry
2. Double the wait time on each failed attempt (max 30 seconds)
3. Reset the wait time after a successful connection
4. Re-subscribe to all channels after reconnecting
5. Fetch missed messages via the REST API based on the last received message timestamp

<CodeGroup>
  ```javascript JavaScript theme={null}
  let reconnectDelay = 1000;
  const maxDelay = 30000;

  function connect() {
    // Session cookie is sent automatically
    const ws = new WebSocket("wss://your-meepa-instance.example.com/api/ws");

    ws.onopen = () => {
      console.log('Connected');
      reconnectDelay = 1000; // reset
      // Re-subscribe to channels
      ws.send(JSON.stringify({
        type: 'subscribe',
        data: { channelIds: myChannels }
      }));
    };

    ws.onclose = () => {
      console.log(`Disconnected, reconnecting in ${reconnectDelay}ms`);
      setTimeout(connect, reconnectDelay);
      reconnectDelay = Math.min(reconnectDelay * 2, maxDelay);
    };

    ws.onerror = (err) => {
      console.error('WebSocket error:', err);
    };

    ws.onmessage = (msg) => {
      const event = JSON.parse(msg.data);
      handleEvent(event);
    };
  }
  ```
</CodeGroup>

## Error Handling

The WebSocket connection will close with the following codes:

| Code | Name             | Description                                     |
| ---- | ---------------- | ----------------------------------------------- |
| 1000 | Normal Closure   | Clean disconnect                                |
| 1006 | Abnormal Closure | Connection lost (network issue, server restart) |
| 1008 | Policy Violation | Authentication failed or origin mismatch        |

If the connection closes abnormally (code 1006 or 1008), clients should attempt to reconnect using the strategy described above. For code 1008, verify that your token is still valid before reconnecting.
