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

# Subscribe to Real-Time Events via WebSocket

> Connect to CA Colombia's WebSocket server to receive real-time events for users and characters. Learn how to subscribe to topics and handle incoming messages.

CA Colombia exposes a WebSocket server that pushes real-time events to connected clients. Rather than polling the REST API for changes, you subscribe to named topics and receive a message the moment something changes — a character's status updates, a user account is modified, or a new character is created. Authentication is handled automatically through your existing session cookie, so no additional credentials are needed beyond an active login.

## Connecting

The WebSocket server is available at:

```text theme={null}
wss://api.cacolombia.co/ws
```

Authentication happens during the WebSocket upgrade handshake. The server reads your session cookie from the request headers and validates it before the connection is established. If the cookie is missing or invalid, the server responds with `HTTP 401 Unauthorized` and closes the socket — no WebSocket connection is opened.

```javascript theme={null}
const ws = new WebSocket('wss://socket.cacolombia.com/');

ws.addEventListener('open', () => {
  console.log('Connected to CA Colombia WebSocket');
});

ws.addEventListener('message', (event) => {
  const msg = JSON.parse(event.data);
  console.log(msg);
});

ws.addEventListener('close', (event) => {
  console.log('Disconnected:', event.code, event.reason);
});
```

<Note>
  Because the session cookie is sent automatically by the browser during the upgrade handshake, you do not need to pass any credentials in the WebSocket constructor or send an authentication message after connecting.
</Note>

## Subscribing to Topics

Once connected, send a JSON message with `type: "subscribe"` and a `topics` array containing the topic names you want to receive events for. You can subscribe to one topic or several at once.

```javascript theme={null}
ws.send(JSON.stringify({
  type: 'subscribe',
  topics: ['characters:update', 'users:update']
}));
```

### Available Topics

| Topic               | Description                                                                                 |
| ------------------- | ------------------------------------------------------------------------------------------- |
| `users:create`      | A new user account was created                                                              |
| `users:update`      | A user account was updated                                                                  |
| `users:delete`      | A user account was removed                                                                  |
| `characters:create` | A new character was created                                                                 |
| `characters:update` | A character was updated, including status changes (e.g. `draft` → `submitted` → `approved`) |
| `characters:delete` | A character was removed                                                                     |

<Tip>
  Subscribe to `characters:update` to get real-time notifications when staff approve or reject a character submission, rather than polling the REST API.
</Tip>

## Unsubscribing from Topics

Send a message with `type: "unsubscribe"` and the topics you no longer want to receive. Topics not listed remain active.

```javascript theme={null}
ws.send(JSON.stringify({
  type: 'unsubscribe',
  topics: ['users:update']
}));
```

## Incoming Message Format

Every event the server pushes to your client follows this structure:

```json theme={null}
{
  "channel": "characters:update",
  "type": "event",
  "data": { ... }
}
```

| Field     | Type   | Description                                                       |
| --------- | ------ | ----------------------------------------------------------------- |
| `channel` | string | The topic that triggered this message, e.g. `"characters:update"` |
| `type`    | string | Always `"event"` for topic broadcasts                             |
| `data`    | object | The payload for the event — its shape depends on the channel      |

A minimal handler that routes messages by channel:

```javascript theme={null}
ws.addEventListener('message', (event) => {
  const { channel, type, data } = JSON.parse(event.data);

  if (type !== 'event') return;

  switch (channel) {
    case 'characters:update':
      handleCharacterUpdate(data);
      break;
    case 'users:update':
      handleUserUpdate(data);
      break;
    default:
      console.log('Unhandled channel:', channel, data);
  }
});
```

If you send a message with an invalid format (for example, a `type` value other than `subscribe` or `unsubscribe`), the server responds with an error object:

```json theme={null}
{
  "error": "Invalid payload format",
  "details": { ... }
}
```

## Connection Keep-Alive

The server sends a WebSocket **ping** frame to every connected client every **30 seconds**. Your WebSocket client must respond with a **pong** frame to confirm the connection is still alive.

Most browser `WebSocket` implementations handle ping/pong frames automatically and transparently — you do not need to write any code for this. If you are using a Node.js `ws` client or a similar library, respond to pings explicitly:

```javascript theme={null}
// Node.js ws library example
ws.on('ping', () => {
  ws.pong();
});
```

If the server does not receive a pong response before the next ping cycle (30 seconds), it marks the connection as dead, terminates the socket, and removes it from the connection pool. Your client should listen for the `close` event and implement reconnection logic with an appropriate back-off strategy.

## Authentication Errors

| Scenario                             | Behaviour                                                         |
| ------------------------------------ | ----------------------------------------------------------------- |
| No session cookie present            | `HTTP 401 Unauthorized` during upgrade — connection is rejected   |
| Session cookie is expired or invalid | `HTTP 401 Unauthorized` during upgrade — connection is rejected   |
| Session expires while connected      | Connection is terminated on the next server-side validation cycle |

<Warning>
  The WebSocket server validates your session at connection time using your access token cookie. If your access token expires while you are connected, the server will terminate the connection. Use the REST OAuth flow to refresh your tokens, then reconnect.
</Warning>
