REST API
Realtime (WebSocket)
Подключение к Reverb по протоколу Pusher, авторизация приватных каналов и события.
Realtime-обновления доставляет Laravel Reverb по протоколу Pusher, поэтому подходит любой Pusher-клиент (pusher-js, Laravel Echo, PusherSwift, …). Параметры — из GET /api/broadcasting/client-config; на проде — wss://ws.minchatapp.com:8443.
{
"enabled": true,
"key": "<app-key>",
"host": "ws.minchatapp.com",
"port": 8443,
"scheme": "https"
}
Подключение#
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
const cfg = await fetch('https://minchatapp.com/api/broadcasting/client-config', {
headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
}).then(r => r.json());
const echo = new Echo({
broadcaster: 'reverb',
Pusher,
key: cfg.key,
wsHost: cfg.host,
wsPort: cfg.port,
wssPort: cfg.port,
forceTLS: cfg.scheme === 'https',
enabledTransports: ['ws', 'wss'],
authEndpoint: 'https://minchatapp.com/broadcasting/auth',
auth: { headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' } },
});
echo.private(`users.${me.id}`)
.listen('.MessageSent', (m) => console.log('new message', m.conversation_id, m.content))
.listen('.ConversationCreated', (c) => console.log('added to', c.name));
echo.private('conversation.12')
.listen('.UserActivity', (a) => console.log(a.name, a.action));
const pusher = new Pusher(cfg.key, {
wsHost: cfg.host, wsPort: cfg.port, wssPort: cfg.port,
forceTLS: true, enabledTransports: ['ws', 'wss'], cluster: '',
channelAuthorization: {
endpoint: 'https://minchatapp.com/broadcasting/auth',
headers: { Authorization: `Bearer ${token}` },
},
});
pusher.subscribe(`private-users.${me.id}`)
.bind('MessageSent', (m) => console.log(m));
Авторизация каналов#
Приватные каналы авторизуются через POST /broadcasting/auth (поля socket_id, channel_name) с тем же Bearer-токеном. Заблокированным отказывается.
curl -X POST https://minchatapp.com/broadcasting/auth \
-H "Authorization: Bearer $TOKEN" -H 'Accept: application/json' \
-d 'socket_id=123.456' -d 'channel_name=private-users.1'
# → {"auth":"<key>:<signature>"}
Каналы#
| Канал | Кто может подписаться | Назначение |
|---|---|---|
private-users.{userId} | Только сам пользователь | Всё, что адресовано пользователю во всех чатах — рекомендуемая единственная подписка |
private-conversation.{id} | Участники чата | События одного открытого чата (набор, прочтения, сообщения) |
qr-login.{hash} | Все (публичный) | QrLoginUpdated для запроса QR-входа |
users.{id} каждого участника — если подписаны на оба, убирайте дубли по событию + id. Имена событий — имена классов (в Echo с точкой: .MessageSent).События#
Новое сообщение. Payload — полный объект сообщения плюс conversation_id, conversation_type, conversation_name, conversation_avatar. is_own/can_delete всегда false — сравнивайте user.id со своим.
{
"id": 55,
"content": "Realtime demo https://example.com",
"type": "text",
"attachments": [],
"link_previews": null,
"reply_to": {
"id": 43,
"content": "Thanks everyone!",
"type": "text",
"attachments": [],
"user": {
"id": 4,
"name": "Gleb Orlov",
"avatar": null
}
},
"user": {
"id": 1,
"name": "Anna Smith",
"avatar": null,
"is_channel": false,
"is_bot": false,
"username": "anna_s"
},
"created_at": "2026-10-22T09:11:15.000000Z",
"edited_at": null,
"conversation_id": 1,
"conversation_type": "group",
"conversation_name": "Design team",
"conversation_avatar": null,
"can_delete": false,
"is_own": false,
"read_receipt": "delivered",
"client_id": null,
"reactions": [],
"forwarded_from": null,
"views_count": null,
"permalink": null,
"system": null,
"card": null,
"group_id": null,
"mentions": [],
"silent": false,
"reply_markup": null,
"expires_at": null
}
Сообщение изменено, обновлён опрос/задачи или клавиатура бота.
{
"id": 55,
"content": "Realtime demo (edited)",
"edited_at": "2026-10-22T09:11:15.000000Z",
"created_at": "2026-10-22T09:11:15.000000Z",
"conversation_id": 1,
"link_previews": null,
"attachments": [],
"type": "text",
"card": null,
"group_id": null,
"reply_markup": null
}
Удалено у всех (или, если пришло только в ваш users.{id}, удалено у вас).
{
"id": 55,
"created_at": "2026-10-22T09:11:15.000000Z",
"conversation_id": 1
}
Изменились реакции. actor_id — кто поставил; emoji_id = null, если сняли. me выставляйте сами, если actor_id — вы.
{
"message_id": 55,
"conversation_id": 1,
"reactions": [
{
"emoji_id": 2,
"set_id": 1,
"set_title": "Min",
"set_active": true,
"shortcode": "heart",
"kind": "unicode",
"unicode": "❤️",
"image_url": null,
"count": 1,
"user_ids": [
1
]
}
],
"actor_id": 1,
"emoji_id": 2
}
Готовы превью ссылок сообщения.
{
"conversation_id": 1,
"message_id": 55,
"link_previews": [
{
"url": "https://example.com",
"provider": "generic",
"title": "example.com",
"description": null,
"image": null,
"site_name": "example.com",
"embed_url": null,
"embed_html": null,
"video_url": null,
"video_type": null,
"video_width": null,
"video_height": null,
"accessible": true,
"meta": []
}
]
}
Участник прочитал чат до last_read_at (галочки прочтения).
{
"conversation_id": 1,
"user_id": 1,
"last_read_at": "2026-10-22T09:11:15+00:00"
}
Печатает / записывает голосовое / отмена. Не для каналов.
{
"conversation_id": 1,
"user_id": 1,
"action": "typing",
"name": "Anna Smith"
}
Вас добавили в чат (или вы создали/вступили). Payload — карточка чата для списка.
{
"id": 1,
"name": "Design team",
"type": "group",
"avatar": null,
"description": "Release discussions",
"username": null,
"visibility": "public",
"share_url": null,
"last_message": null,
"unread_count": 0
}
Изменились настройки чата или ваша роль. Payload персональный (user_role, is_owner, can_send_messages, can_pin, …).
{
"id": 1,
"name": "Design team",
"avatar": null,
"description": "Release discussions",
"type": "group",
"user_role": "admin",
"is_owner": true,
"can_send_messages": true,
"members_can_pin": false,
"can_pin": true,
"can_manage_subscribers": false,
"username": null,
"visibility": "public",
"share_url": null,
"hidden_from_discovery": false,
"history_ttl": 0
}
Чат удалён или вас исключили.
{
"id": 12
}
Mute изменён на другом вашем устройстве.
{
"conversation_id": 1,
"muted_until": "2026-10-22T10:11:15+00:00"
}
Изменились закреплённые сообщения.
{
"conversation_id": 1,
"pinned_messages": [
{
"id": 55,
"content": "Realtime demo (edited)",
"type": "text",
"attachments": [],
"user": {
"id": 1,
"name": "Anna Smith",
"avatar": null
},
"created_at": "2026-10-22T09:11:15.000000Z",
"pinned_at": "2026-10-22T09:11:15+00:00",
"pinned_by": 1
}
]
}
Черновик изменён на другом устройстве.
{
"conversation_id": 1,
"draft": {
"content": "typing a draft…",
"reply_to_message_id": null,
"updated_at": "2026-10-22T09:11:15+00:00"
}
}
Изменилось число ваших отложенных сообщений в чате.
{
"conversation_id": 1,
"pending_count": 5
}
Изменился счётчик непрочитанных упоминаний чата.
{
"conversation_id": 1,
"unread_mentions_count": 2
}
Сработало напоминание (due:true) — покажите в приложении.
{
"reminder": {
"id": 3,
"message_id": 45,
"conversation_id": 1,
"conversation_name": "Design team",
"sender_name": "Anna Smith",
"preview": "Hello @Boris Petrov!",
"remind_at": "2026-11-02T10:00:00+00:00",
"status": "sent"
},
"due": true
}
Сессии (id токенов) завершены. Если ваша в списке — выйдите локально.
{
"session_ids": [
19,
23
]
}
Запрошен одноразовый код входа в этот аккаунт; покажите его в авторизованном приложении.
{
"code": "482913",
"expires_in": 600
}
Входящий звонок.
{
"call_id": "7c1d…",
"conversation_id": 3,
"conversation_type": "private",
"conversation_name": "Anna Smith",
"conversation_avatar": null,
"room": "conv-3",
"mode": "video",
"ring_ttl": 45,
"caller": {
"id": 1,
"name": "Anna Smith",
"avatar": null
},
"started_at": "2026-10-22T09:30:00.000Z"
}
На звонок ответили на одном из ваших устройств — остановите звонок на остальных.
{
"call_id": "7c1d…",
"conversation_id": 3
}
Звонок завершён. reason: ended, cancelled, declined, timeout, busy, unavailable.
{
"call_id": "7c1d…",
"conversation_id": 3,
"reason": "ended",
"by_user_id": 1
}
Изменился статус скачивания видео.
{
"id": 7,
"url": "https://www.youtube.com/watch?v=…",
"status": "completed",
"file_url": "https://minchatapp.com/api/videos/<token>",
"title": "Demo",
"duration": 212,
"error_message": null,
"code": null
}
Телефон подтвердил или отклонил QR-запрос: status — confirmed или declined. При confirmed вызовите /auth/qr/poll за токеном.
{
"status": "confirmed"
}