261 lines
4.7 KiB
Markdown
261 lines
4.7 KiB
Markdown
# Realtime WebSocket API
|
|
|
|
WebSocket endpoint:
|
|
|
|
- `GET /api/v1/realtime/ws?token=<access_token>`
|
|
|
|
Authentication:
|
|
|
|
- Pass a valid **access token** as query parameter `token`.
|
|
- If token is missing/invalid, server closes with `1008` (policy violation).
|
|
|
|
## 1. Message envelope
|
|
|
|
All incoming and outgoing events use the same envelope:
|
|
|
|
```json
|
|
{
|
|
"event": "event_name",
|
|
"payload": {},
|
|
"timestamp": "2026-03-08T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
`timestamp` is present in outgoing events from the server.
|
|
|
|
## 2. Incoming events (client -> server)
|
|
|
|
## 2.1 `ping`
|
|
|
|
```json
|
|
{
|
|
"event": "ping",
|
|
"payload": {}
|
|
}
|
|
```
|
|
|
|
Server response: `pong`
|
|
|
|
## 2.2 `send_message`
|
|
|
|
```json
|
|
{
|
|
"event": "send_message",
|
|
"payload": {
|
|
"chat_id": 10,
|
|
"type": "text",
|
|
"text": "Hello",
|
|
"temp_id": "tmp-1",
|
|
"client_message_id": "client-msg-1",
|
|
"reply_to_message_id": null
|
|
}
|
|
}
|
|
```
|
|
|
|
Supported `type` values:
|
|
|
|
- `text`, `image`, `video`, `audio`, `voice`, `file`, `circle_video`
|
|
|
|
Web note: browser client sends `text/image/video/audio/voice/file`. `circle_video` is reserved for mobile clients.
|
|
|
|
## 2.3 `typing_start` / `typing_stop`
|
|
|
|
```json
|
|
{
|
|
"event": "typing_start",
|
|
"payload": {
|
|
"chat_id": 10
|
|
}
|
|
}
|
|
```
|
|
|
|
## 2.4 `message_delivered` / `message_read`
|
|
|
|
```json
|
|
{
|
|
"event": "message_read",
|
|
"payload": {
|
|
"chat_id": 10,
|
|
"message_id": 150
|
|
}
|
|
}
|
|
```
|
|
|
|
## 3. Outgoing events (server -> client)
|
|
|
|
## 3.1 `connect`
|
|
|
|
Sent after successful websocket registration:
|
|
|
|
```json
|
|
{
|
|
"event": "connect",
|
|
"payload": {
|
|
"connection_id": "uuid"
|
|
},
|
|
"timestamp": "2026-03-08T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
## 3.2 `pong`
|
|
|
|
Response to `ping`:
|
|
|
|
```json
|
|
{
|
|
"event": "pong",
|
|
"payload": {},
|
|
"timestamp": "2026-03-08T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
## 3.3 `receive_message`
|
|
|
|
```json
|
|
{
|
|
"event": "receive_message",
|
|
"payload": {
|
|
"chat_id": 10,
|
|
"message": {
|
|
"id": 123,
|
|
"chat_id": 10,
|
|
"sender_id": 1,
|
|
"reply_to_message_id": null,
|
|
"forwarded_from_message_id": null,
|
|
"type": "text",
|
|
"text": "Hello",
|
|
"created_at": "2026-03-08T12:00:00Z",
|
|
"updated_at": "2026-03-08T12:00:00Z"
|
|
},
|
|
"temp_id": "tmp-1",
|
|
"client_message_id": "client-msg-1",
|
|
"sender_id": 1
|
|
},
|
|
"timestamp": "2026-03-08T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
## 3.4 Typing events
|
|
|
|
`typing_start` and `typing_stop`:
|
|
|
|
```json
|
|
{
|
|
"event": "typing_start",
|
|
"payload": {
|
|
"chat_id": 10,
|
|
"user_id": 2
|
|
},
|
|
"timestamp": "2026-03-08T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
## 3.5 Delivery/read events
|
|
|
|
`message_delivered` and `message_read`:
|
|
|
|
```json
|
|
{
|
|
"event": "message_read",
|
|
"payload": {
|
|
"chat_id": 10,
|
|
"message_id": 150,
|
|
"user_id": 2,
|
|
"last_delivered_message_id": 150,
|
|
"last_read_message_id": 150
|
|
},
|
|
"timestamp": "2026-03-08T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
## 3.6 Presence events
|
|
|
|
### `user_online`
|
|
|
|
```json
|
|
{
|
|
"event": "user_online",
|
|
"payload": {
|
|
"chat_id": 10,
|
|
"user_id": 2,
|
|
"is_online": true
|
|
},
|
|
"timestamp": "2026-03-08T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
### `user_offline`
|
|
|
|
```json
|
|
{
|
|
"event": "user_offline",
|
|
"payload": {
|
|
"chat_id": 10,
|
|
"user_id": 2,
|
|
"is_online": false,
|
|
"last_seen_at": "2026-03-08T12:00:00Z"
|
|
},
|
|
"timestamp": "2026-03-08T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
## 3.7 `chat_updated`
|
|
|
|
Sent when chat metadata/membership/roles/title changes:
|
|
|
|
```json
|
|
{
|
|
"event": "chat_updated",
|
|
"payload": {
|
|
"chat_id": 10
|
|
},
|
|
"timestamp": "2026-03-08T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
## 3.8 `chat_deleted`
|
|
|
|
Sent when a chat is removed for all members/subscribers:
|
|
|
|
```json
|
|
{
|
|
"event": "chat_deleted",
|
|
"payload": {
|
|
"chat_id": 10
|
|
},
|
|
"timestamp": "2026-03-08T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
## 3.9 `error`
|
|
|
|
Validation/runtime error during WS processing:
|
|
|
|
```json
|
|
{
|
|
"event": "error",
|
|
"payload": {
|
|
"detail": "Invalid event payload"
|
|
},
|
|
"timestamp": "2026-03-08T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
## 4. Disconnect behavior
|
|
|
|
- On disconnect, server unregisters connection.
|
|
- When last connection for a user closes, server marks user offline and sends `user_offline` to related chats.
|
|
|
|
## 5. Practical client recommendations
|
|
|
|
- Keep one active socket per tab/session.
|
|
- Send periodic `ping` and expect `pong`.
|
|
- Reconnect with exponential backoff.
|
|
- On `chat_updated`, refresh chat metadata via REST (`GET /api/v1/chats` or `GET /api/v1/chats/{chat_id}`).
|
|
- On `chat_deleted`, remove the chat from local state immediately and clear active-chat selection if needed.
|
|
- On reconnect/visibility restore, reconcile state by reloading already-opened chats/messages via REST
|
|
to recover missed `message_deleted`/delivery updates after transient disconnects or backend restarts.
|
|
- For browser notifications, mentions (`@username`) should be treated as high-priority and can bypass
|
|
per-chat-type notification toggles in client preferences.
|
|
- Use REST message history endpoints for pagination; WS is realtime transport, not history source.
|