Skip to content

Справочник маршрутов ​

Базовый адрес — https://emotify.ru/api/v1. Все тела — JSON, заголовок Content-Type: application/json обязателен у запросов с телом.

Авторизация — Authorization: Bot <ключ> (подробнее). Маршруты /users/@me… работают с токеном пользователя (вход через Emotify).

Все маршруты ​

Колонка «Scope» — какой доступ нужно отметить при выпуске ключа. Без него маршрут отвечает 403, даже когда роль бота на канале действие разрешает.

МетодПутьScopeЧто делает
GET/meлюбой ключКто я и где я стою
POST/rooms/:roomId/messagesmessages:writeОтправить сообщение
GET/rooms/:roomId/messagesmessages:readИстория комнаты
PATCH/rooms/:roomId/messages/:messageIdmessages:writeИзменить своё сообщение
DELETE/rooms/:roomId/messages/:messageIdmessages:writeУдалить своё сообщение
POST/rooms/:roomId/messages/:messageId/reactionsmessages:writeПоставить реакцию
DELETE/rooms/:roomId/messages/:messageId/reactionsmessages:writeСнять реакцию
GET/channels/:channelIdchannel:readСведения о канале
GET/channels/:channelId/roomschannel:readКомнаты канала
GET/channels/:channelId/memberschannel:readУчастники канала
POST/rooms/:roomId/voicevoiceВойти в голосовую
DELETE/rooms/:roomId/voicevoiceВыйти из голосовой
GET/events/tokeneventsТокен подписки на события
PUT/webhookeventsЗадать адрес вебхука
GET/webhookeventsСостояние вебхука
DELETE/webhookeventsУдалить вебхук
POST/rooms/:roomId/messages/:messageId/pinmoderationЗакрепить сообщение
DELETE/rooms/:roomId/messages/:messageId/pinmoderationОткрепить сообщение
PUT/channels/:channelId/members/:userId/chat-mutemoderationЗапретить писать в чат
DELETE/channels/:channelId/members/:userId/chat-mutemoderationСнять запрет на чат
PUT/channels/:channelId/members/:userId/voice-mutemoderationЗамьютить микрофон
DELETE/channels/:channelId/members/:userId/voice-mutemoderationСнять мут микрофона
DELETE/channels/:channelId/members/:userId/voicemoderationОтключить от голосовой
PUT/channels/:channelId/members/:userId/roommoderationПеренести в другую комнату
DELETE/channels/:channelId/members/:userIdmoderationИсключить с канала
GET/commandscommandsЧто объявлено сейчас
PUT/commandscommandsОбъявить команды
GET/users/@meтокен пользователяПрофиль вошедшего человека
GET/users/@me/channelsтокен пользователяКаналы вошедшего человека

Профиль бота ​

GET /me ​

Кто вы и на каких каналах установлены. Хороший первый запрос: им проверяют ключ.

bash
curl https://emotify.ru/api/v1/me -H "Authorization: Bot $BOT_TOKEN"

Ответ 200:

json
{
  "application": {
    "id": "16c6e6b1-2b8d-4665-ba47-3d197a4a6029",
    "name": "Пример бота",
    "owner": { "id": "b49c3095-…", "name": "Z3oM" }
  },
  "bot": {
    "id": "a2d78dd9-…",
    "name": "Пример бота",
    "avatar": "default_avatar.jpg",
    "avatarVersion": 1
  },
  "channels": [
    {
      "id": "4facb875-…",
      "name": "Тестовый сервак",
      "role": { "systemName": "CHANNEL_BOT", "name": "Бот" }
    }
  ]
}

channels — каналы, где приложение установлено. Пустой список означает, что бота ещё никуда не добавили.


Канал ​

GET /channels/{channelId} ​

Название и описание канала.

bash
curl https://emotify.ru/api/v1/channels/$CHANNEL -H "Authorization: Bot $BOT_TOKEN"

Ответ 200:

json
{
  "id": "4facb875-…",
  "name": "Тестовый сервак",
  "description": "Песочница для ботов"
}

description может быть null. Ошибки: 403 — приложение не установлено на этот канал.

GET /channels/{channelId}/rooms ​

Комнаты канала и их категории. Отсюда бот узнаёт, куда писать и куда заходить, — идентификаторы комнат больше не нужно брать руками из адресной строки.

bash
curl https://emotify.ru/api/v1/channels/$CHANNEL/rooms \
  -H "Authorization: Bot $BOT_TOKEN"

Ответ 200:

json
{
  "rooms": [
    {
      "id": "a0d4cc29-…",
      "name": "Приветственный",
      "type": "text",
      "categoryId": "b71f0a35-…",
      "sort": 1,
      "writeRestricted": false
    },
    {
      "id": "29004975-…",
      "name": "Гостинная",
      "type": "voice",
      "categoryId": null,
      "sort": 2,
      "writeRestricted": false,
      "userLimit": 10,
      "users": [
        { "id": "b49c3095-…", "name": "Z3oM", "channelNickname": "Хозяин" }
      ]
    }
  ],
  "categories": [
    { "id": "b71f0a35-…", "name": "Общение", "sort": 1 }
  ]
}
Поле комнатыОписание
typetext — текстовая · voice — голосовая · stream — чат трансляции
categoryIdкатегория из categories, либо null
sortпорядок внутри канала, как в портале
writeRestrictedписать в комнату могут не все — у роли бота может не быть права
userLimitтолько у голосовых: предел участников, null — без предела
usersтолько у голосовых: кто сейчас в комнате; пустой массив — никого

Приватные комнаты не отдаются

Их нет в ответе, даже если бот в них допущен. Состав приватных голосовых тоже недоступен.

Ошибки: 403 — приложение не установлено на канал.

Типичное применение — найти комнату по имени и написать в неё:

js
const { rooms } = await api(`/channels/${channelId}/rooms`).then((r) => r.json())

const general = rooms.find((room) => room.type === 'text' && room.name === 'Общий')
if (general) {
  await api(`/rooms/${general.id}/messages`, {
    method: 'POST',
    body: JSON.stringify({ channelId, text: 'Бот на связи' })
  })
}

Или узнать, есть ли кто-нибудь в голосовых:

js
const busy = rooms.filter((room) => room.type === 'voice' && room.users.length > 0)
console.log(busy.map((room) => `${room.name}: ${room.users.length}`))

Сообщения ​

POST /rooms/{roomId}/messages — отправить ​

ПараметрГдеТипОбязателенОписание
roomIdпутьuuidдатекстовая комната
channelIdтелоuuidдаканал, которому принадлежит комната
textтелоstringдатекст, 1–4000 символов
replyToIdтелоuuidнетсообщение, на которое отвечаем
bash
curl -X POST https://emotify.ru/api/v1/rooms/$ROOM/messages \
  -H "Authorization: Bot $BOT_TOKEN" -H "Content-Type: application/json" \
  -d "{\"channelId\":\"$CHANNEL\",\"text\":\"Привет!\"}"
js
await fetch(`https://emotify.ru/api/v1/rooms/${roomId}/messages`, {
  method: 'POST',
  headers: { Authorization: `Bot ${token}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ channelId, text: 'Привет!' })
})

Ответ 201:

json
{
  "id": "3f8a1c62-…",
  "text": "Привет!",
  "createdAt": "2026-09-19T12:00:00.000Z",
  "author": { "id": "a2d78dd9-…", "name": "Пример бота" }
}

Ошибки: 403 — нет права писать в комнаты · 422 — комната не текстовая или не принадлежит channelId · 429 — больше 20 сообщений в минуту.

В тексте работает разметка Markdown — так же, как у людей: **жирный**, *курсив*, ``код``, ссылки.

GET /rooms/{roomId}/messages — история ​

ПараметрГдеТипОбязателенОписание
roomIdпутьuuidдатекстовая комната
channelIdqueryuuidдаканал комнаты
limitqueryчислонет1–100
beforeIdqueryuuidнетотдать сообщения старее этого — для листания
bash
curl "https://emotify.ru/api/v1/rooms/$ROOM/messages?channelId=$CHANNEL&limit=50" \
  -H "Authorization: Bot $BOT_TOKEN"

Ответ 200:

json
{
  "messages": [
    {
      "id": "3f8a1c62-…",
      "text": "Привет!",
      "createdAt": "2026-09-19T12:00:00.000Z",
      "author": { "id": "…", "name": "Z3oM" }
    }
  ],
  "hasMore": true
}

Сообщения идут от новых к старым. Чтобы получить следующую страницу, передайте beforeId — id последнего сообщения из предыдущего ответа. Удалённые сообщения в выдачу не попадают.

Ошибки: 403 — нет права читать историю.

PATCH /rooms/{roomId}/messages/{messageId} — изменить своё сообщение ​

ПараметрГдеТипОбязателенОписание
messageIdпутьuuidдасообщение, написанное ботом
channelIdтелоuuidдаканал комнаты
textтелоstringдановый текст, 1–4000 символов
bash
curl -X PATCH https://emotify.ru/api/v1/rooms/$ROOM/messages/$MSG \
  -H "Authorization: Bot $BOT_TOKEN" -H "Content-Type: application/json" \
  -d "{\"channelId\":\"$CHANNEL\",\"text\":\"Сборка завершена ✅\"}"

Ответ 200:

json
{
  "id": "3f8a1c62-…",
  "text": "Сборка завершена ✅",
  "editedAt": "2026-09-19T12:08:22.521Z"
}

Ошибки: 403 — сообщение чужое, удалено, не найдено или у роли нет права edit_text_msg_you_own. Эти случаи снаружи неразличимы, поэтому ответ один на все.

Одно сообщение вместо десяти

Правка удобна для всего, что меняется: прогресс задачи, счёт в игре, статус деплоя. Бот пишет сообщение один раз и обновляет его, не засоряя комнату.

Править можно 30 минут

После этого правка своего сообщения перестаёт проходить — ограничение общее для людей и ботов.

DELETE /rooms/{roomId}/messages/{messageId} — удалить своё сообщение ​

ПараметрГдеТипОбязателен
messageIdпутьuuidда
channelIdтелоuuidда
bash
curl -X DELETE https://emotify.ru/api/v1/rooms/$ROOM/messages/$MSG \
  -H "Authorization: Bot $BOT_TOKEN" -H "Content-Type: application/json" \
  -d "{\"channelId\":\"$CHANNEL\"}"

Ответ 204 без тела. 403 — сообщение чужое или нет права delete_text_msg_you_own.

Удаление тратит квоту записи (20 в минуту), как и отправка.

POST /rooms/{roomId}/messages/{messageId}/reactions — поставить реакцию ​

ПараметрГдеТипОбязателенОписание
messageIdпутьuuidдасообщение
channelIdтелоuuidдаканал
emojiтелоstringдаэмодзи, например "👍"
bash
curl -X POST https://emotify.ru/api/v1/rooms/$ROOM/messages/$MSG/reactions \
  -H "Authorization: Bot $BOT_TOKEN" -H "Content-Type: application/json" \
  -d "{\"channelId\":\"$CHANNEL\",\"emoji\":\"👍\"}"

Ответ 201: { "emoji": "👍", "messageId": "…" }

DELETE /rooms/{roomId}/messages/{messageId}/reactions — снять реакцию ​

Те же параметры. Снимает реакцию, поставленную ботом. Ответ 204 без тела.


Участники ​

GET /channels/{channelId}/members ​

Состав канала: люди и боты.

bash
curl https://emotify.ru/api/v1/channels/$CHANNEL/members \
  -H "Authorization: Bot $BOT_TOKEN"

Ответ 200:

json
{
  "members": [
    {
      "id": "b49c3095-…",
      "name": "Z3oM",
      "channelNickname": "Хозяин",
      "online": true,
      "isBot": false,
      "avatar": "412d5d2b-….jpg",
      "avatarVersion": 3,
      "roles": [{ "systemName": "CHANNEL_OWNER", "name": "Владелец канала" }]
    }
  ],
  "online": 12,
  "total": 294,
  "hasMore": true
}
ПолеОписание
membersучастники: все онлайн плюс часть офлайновых
onlineсколько участников канала сейчас в сети
totalсколько участников на канале всего
hasMoreесть ли ещё офлайновые участники за пределами выдачи

Если нужен только счётчик, читайте online и total — перебирать members не нужно: на больших каналах офлайновая часть приходит не целиком.

Боты тоже участники

isBot: true — это другое приложение на том же канале. Боты входят в total и в online, так что «сколько здесь людей» считается фильтром по isBot — см. рецепт.

channelNickname — ник участника на этом канале (может быть null); name — его общее имя в Emotify. Показывайте ник, если он есть, иначе имя — так делает и сам портал.


Голос ​

POST /rooms/{roomId}/voice — войти в голосовую ​

ПараметрГдеТипОбязателенОписание
roomIdпутьuuidдаголосовая комната
channelIdтелоuuidдаканал комнаты
bash
curl -X POST https://emotify.ru/api/v1/rooms/$VOICE_ROOM/voice \
  -H "Authorization: Bot $BOT_TOKEN" -H "Content-Type: application/json" \
  -d "{\"channelId\":\"$CHANNEL\"}"

Ответ 200:

json
{
  "roomId": "29004975-…",
  "token": "eyJhbGciOi…",
  "url": "https://emotify.ru/ws/",
  "canPublish": true,
  "identity": "a2d78dd9-…"
}

После этого запроса бот уже виден в комнате. Чтобы он был и слышен, подключитесь к LiveKit полученным токеном и публикуйте звук, например через @livekit/rtc-node:

js
import { Room, AudioSource, LocalAudioTrack, TrackPublishOptions } from '@livekit/rtc-node'

const { token, url } = await joinVoice(roomId, channelId) // запрос выше

const room = new Room()
await room.connect(url, token)

const source = new AudioSource(48000, 1)
const track = LocalAudioTrack.createAudioTrack('sound', source)
await room.localParticipant.publishTrack(track, new TrackPublishOptions())
// дальше пишите PCM-кадры в source

canPublish: false означает, что у роли бота нет права говорить — публикация не заработает, пока администратор не выдаст право talk_voice_room.

Ошибки: 422 — комната не голосовая или не принадлежит каналу.

DELETE /rooms/{roomId}/voice — выйти ​

Без тела. Ответ 204. Бот исчезает из комнаты.


Команды ​

Список того, что умеет бот. Портал показывает его людям в трёх местах: кнопкой у поля ввода, подсказкой при наборе / и на экране установки приложения на канал.

Команда — это обычное сообщение, начинающееся с /. Портал его не разбирает и никуда не маршрутизирует: событие о сообщении по-прежнему приходит всем ботам канала, и разбирать текст должен сам бот. Если два бота объявили /погода, ответят оба — в списке каждая команда показана вместе с именем своего бота.

Требуется scope commands — см. ключ и его scope.

Реагируете на команды — обязаны их объявить

Это требование платформы, а не пожелание. Незарегистрированной команды для портала не существует: узнать о ней можно только от вас лично.

Люди не читают документацию к боту — они нажимают кнопку у поля ввода и набирают /. Бот, который молча ждёт !погода, для участника канала неотличим от сломанного: он ставится, висит в списке участников и ничего не делает.

Объявляйте список при каждом запуске программы и держите его рядом с обработчиками: тогда объявленное и обрабатываемое не разъезжаются.

Если команд нет ​

Объявлять нечего — это нормальный случай, и портал с ним работает честно:

БотЧто делает
Бот-уведомитель (только пишет: CI, мониторинг, заказы)команд у него нет по природе — не объявляйте ничего
Голосовой бот без текстовых командто же самое
Бот, реагирующий на обычную речь (эхо, автоответчик)команд нет, но опишите поведение администратору при установке

Что видят люди, когда команд нет:

  • кнопки команд у поля ввода не появляется вовсе — не серой, не пустой, её просто нет. Кнопка, открывающая пустоту, хуже отсутствующей;
  • набор / ничего не подсказывает — сообщение отправляется как обычный текст;
  • на экране установки приложения и в кабинете канала блок команд не показывается.

⚠️ Всё это видно на уровне канала: если на канале стоят два бота и команды объявил только один, кнопка есть, и в ней перечислены команды этого одного.

Пустой массив стирает объявленное — бот исчезает из подсказок, оставаясь на канале:

bash
curl -X PUT https://emotify.ru/api/v1/commands   -H "Authorization: Bot $BOT_TOKEN" -H "Content-Type: application/json"   -d '{"commands":[]}'

PUT /commands — объявить команды ​

Заменяет список целиком: что прислали, то и стало. Отдельного «добавь одну» нет намеренно — иначе команда, удалённая из кода бота, осталась бы висеть в меню навсегда.

ПараметрТипОбязателенОписание
commandsarrayдадо 25 команд; порядок в массиве = порядок показа
commands[].namestringданачинается с /, дальше буквы (латиница или кириллица), цифры и дефис, до 32 символов
commands[].descriptionstringдачто делает команда, до 120 символов
commands[].usagestringнетподсказка аргумента, например <город>, до 60 символов
bash
curl -X PUT https://emotify.ru/api/v1/commands \
  -H "Authorization: Bot $BOT_TOKEN" -H "Content-Type: application/json" \
  -d '{
    "commands": [
      { "name": "/погода", "description": "Погода в городе", "usage": "<город>" },
      { "name": "/помощь", "description": "Что я умею" }
    ]
  }'
js
await fetch('https://emotify.ru/api/v1/commands', {
  method: 'PUT',
  headers: {
    Authorization: `Bot ${process.env.BOT_TOKEN}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    commands: [
      { name: '/погода', description: 'Погода в городе', usage: '<город>' },
      { name: '/помощь', description: 'Что я умею' }
    ]
  })
})

Ответ 200:

json
{
  "commands": [
    {
      "name": "/погода",
      "description": "Погода в городе",
      "usage": "<город>",
      "position": 1
    },
    { "name": "/помощь", "description": "Что я умею", "usage": null, "position": 2 }
  ]
}

position присваивает сервер по порядку массива — присылать его не нужно.

Пустой массив стирает все команды: бот перестаёт показываться в подсказках.

КодКогда
400имя без / или с недопустимыми символами, описание пустое или длиннее 120, больше 25 команд, одна и та же команда дважды
403у ключа нет scope commands — перевыпустите ключ в кабинете приложения
429превышена квота записи

GET /commands — что объявлено сейчас ​

bash
curl https://emotify.ru/api/v1/commands -H "Authorization: Bot $BOT_TOKEN"

Ответ 200 — тот же формат, что у PUT.

Команды принадлежат приложению, а не каналу: объявляются один раз и действуют на всех каналах, где бот установлен.

Объявляйте при старте

Держите список рядом с кодом обработчиков и отправляйте его при запуске бота — тогда объявленное и обрабатываемое не разъезжаются.


События: токен подписки ​

GET /events/token ​

Токен для получения событий по WebSocket — как им пользоваться, описано на странице события.

Ответ 200:

json
{
  "token": "eyJhbGciOi…",
  "channel": "bot:16c6e6b1-…",
  "url": "wss://emotify.ru/cf/connection/websocket",
  "expiresIn": 21600
}

Токен живёт 6 часов. Запрашивайте новый заранее — этим же маршрутом.


Вебхук ​

Управление адресом доставки событий. Формат доставок и проверка подписи — на странице события.

PUT /webhook — задать адрес ​

ПараметрТипОбязателенОписание
urlstringдапубличный https-адрес, до 500 символов
rotateSecretbooleanнетtrue — выпустить новый ключ подписи
bash
curl -X PUT https://emotify.ru/api/v1/webhook \
  -H "Authorization: Bot $BOT_TOKEN" -H "Content-Type: application/json" \
  -d '{"url":"https://bot.example.com/emotify"}'

Ответ 201 (при создании) или 200 (при обновлении):

json
{
  "url": "https://bot.example.com/emotify",
  "active": true,
  "secret": "whsec_…",
  "signatureHeader": "X-Emotify-Signature"
}

secret приходит только при создании и при rotateSecret: true — сохраните его.

localhost и адреса частных сетей не принимаются: нужен публичный хост. Для локальной отладки используйте туннель (ngrok и подобные) или WebSocket-транспорт.

GET /webhook — состояние ​

Ответ 200:

json
{
  "url": "https://bot.example.com/emotify",
  "active": true,
  "failedStreak": 0,
  "createdAt": "2026-09-18T10:00:00.000Z"
}

active: false с ненулевым failedStreak означает, что доставки отключены после серии неудач — почините приёмник и задайте адрес заново через PUT.

404 — вебхук не настроен.

DELETE /webhook — удалить ​

Ответ 204. События перестают уходить.


Маршруты с токеном пользователя ​

Работают с Authorization: Bearer <токен>, полученным по входу через Emotify. Ключ приложения здесь не подходит.

GET /users/@me ​

Требует scope emotify.identify.

json
{
  "id": "b3f4d17e-…",
  "name": "KatrinKa 1",
  "avatar": "412d5d2b-….jpg",
  "avatarVersion": 10,
  "scopes": ["openid", "emotify.identify"]
}

GET /users/@me/channels ​

Требует scope emotify.channels.read.

json
{
  "channels": [
    {
      "id": "4facb875-…",
      "name": "Тестовый сервак",
      "image": "channel-icon.jpg",
      "avatarVersion": 2,
      "role": { "systemName": "CHANNEL_OWNER", "name": "Владелец канала" }
    }
  ]
}

Документация внешнего API Emotify