Перейти к содержимому
CalabДокументация

Сообщения

Отправка, правка и удаление сообщений, ответы, пересылка, реакции, закрепы, отметка о прочтении и расшифровки записей встреч.

Бот пишет в комнаты и личные сообщения теми же маршрутами, что и приложение: отправляет текст, файлы и стикеры, отвечает на сообщения, пересылает их, ставит реакции, закрепляет и читает историю. Здесь же — чтение одного сообщения по id (удобно для ответа на карточку) и полная расшифровка записи встречи.

Права проверяются по комнате, как для человека:

ДействиеПраво
читать историю, одно сообщение, закрепы, расшифровкуVIEW_ROOM
писать, ставить реакцииSEND_MESSAGES (+ ATTACH_FILES для вложений)
править своё сообщениеавтор
удалять чужие, закреплятьMANAGE_MESSAGES (в личных сообщениях закрепляют оба участника)
пересылатьVIEW_ROOM в исходной комнате и SEND_MESSAGES в целевой

Маршруты

МетодПутьЧто делает
GET/api/rooms/{id}/messagesИстория комнаты, новые сверху: ?before=&after=&limit= (limit ≤ 100); ?q= — полнотекстовый поиск по комнате (VIEW_ROOM)
GET/api/rooms/{id}/messages/{messageId}Одно сообщение — голый Message без обёртки (VIEW_ROOM)
POST/api/rooms/{id}/messagesОтправить сообщение → 201 {message} (SEND_MESSAGES)
POST/api/rooms/{id}/messages/{mid}/forwardПереслать в другую комнату {toRoomId} → 201 {message} с полем forward
PATCH/api/messages/{id}Править своё сообщение: текст и/или inline-клавиатуру → {message}
DELETE/api/messages/{id}Удалить своё или чужое (MANAGE_MESSAGES) → 204
GET/api/messages/{id}/reactions/{emoji}Кто поставил реакцию: ?after=&limit= (1..100, по умолчанию 25) → {users, hasMore}
PUT/api/messages/{id}/reactions/{emoji}Поставить реакцию (emoji в URL-кодировке) → 204
DELETE/api/messages/{id}/reactions/{emoji}Снять свою реакцию → 204
PUT/api/messages/{id}/pinЗакрепить → 204 (MANAGE_MESSAGES)
DELETE/api/messages/{id}/pinОткрепить → 204
GET/api/rooms/{id}/pinsЗакреплённые сообщения, последние закреплённые сверху → {messages}
PUT/api/rooms/{id}/readСдвинуть отметку о прочтении {messageId} → 204
PUT/api/messages/{id}/embeds-hiddenСкрыть / показать превью ссылок {hidden} (автор или MANAGE_MESSAGES) → {message}
GET/api/rooms/{id}/recordings/{rid}/transcriptПолная расшифровка записи встречи (VIEW_ROOM)

Недоступно ботам

На эти маршруты бот получает 403 FORBIDDEN, reason: "BOT_NOT_ALLOWED":

  • POST /api/messages/{id}/interactions — нажатие inline-кнопки: нажимают только люди (бот получает результат как событие botCallback, см. Команды и кнопки);
  • GET /api/unfurl, GET /api/unfurl/image — превью ссылок строит приложение, не бот;
  • POST /api/rooms/{id}/recordings/{rid}/recheck, …/reupload, DELETE /api/rooms/{id}/recordings/{rid} — повтор обработки, перезагрузка и удаление записей.

Отправить сообщение

Тело — CreateMessageRequest:

ПолеТипОписание
contentstringТекст, ≤ 4000 символов. Упоминание — @<id пользователя>
attachmentIdsstring[]id заранее загруженных файлов, ≤ 20 (см. Файлы)
replyToIdstringid сообщения, на которое это ответ
noncestringКлюч идемпотентности, ≤ 64 символов
stickerIdstringСтикер вместо текста (см. Стикеры)
inlineKeyboardobjectInline-кнопки (см. Команды и кнопки)

nonce защищает от дублей: повтор запроса с тем же nonce вернёт то же сообщение с кодом 200 вместо 201. Генерируйте его один раз на сообщение и повторяйте при сетевых ошибках и 429.

curl -s -X POST $CALAB/api/rooms/$ROOM/messages \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"content": "Привет! Я бот.", "nonce": "hello-1"}'

Ответ 201:

JSON
{
  "message": {
    "id": "0192a1c4-7b2e-7d10-9a51-3f2b8c0e1d77",
    "roomId": "0191f0aa-2c3d-7e4f-8a9b-1c2d3e4f5a6b",
    "authorId": "0192a1b2-3c4d-7e5f-8a9b-0c1d2e3f4a5b",
    "content": "Привет! Я бот.",
    "attachments": [],
    "replyToId": "",
    "nonce": "hello-1",
    "createdAt": "2026-10-14T10:01:02.345Z",
    "reactions": [],
    "kind": "MESSAGE_KIND_UNSPECIFIED",
    "embedsHidden": false
  }
}

Поля со значениями по умолчанию всегда присутствуют в ответе; поля uint64 (например, keyboardRevision, size файла) приходят строками.

Ответ и упоминание

curl -s -X POST $CALAB/api/rooms/$ROOM/messages \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d "{\"content\": \"Готово, @$USER\", \"replyToId\": \"$MSG\", \"nonce\": \"r-1\"}"

Файл во вложении

Сначала загрузите файл в пространство (или в личные сообщения), затем передайте его id в attachmentIds. SDK делает оба шага сам.

curl -s -X POST $CALAB/api/workspaces/$WS/files -H "Authorization: Bearer $TOKEN" -F file=@report.pdf
# {"file": {"id": "0192b3…", "name": "report.pdf", "mime": "application/pdf", "size": "48213", "url": "/api/files/0192b3…", …}}
curl -s -X POST $CALAB/api/rooms/$ROOM/messages \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"content": "Отчёт", "attachmentIds": ["0192b3…"], "nonce": "rep-1"}'

Править и удалять

PATCH /api/messages/{id} принимает UpdateMessageRequest {content, inlineKeyboard, preserveContent}:

  • content заменяет текст целиком; пустой текст допустим, только если у сообщения есть вложения;
  • inlineKeyboard не передан — клавиатура остаётся; {} или {"rows": []} — снимается;
  • preserveContent: true — правка только клавиатуры: текст не трогается, content нужно опустить;
  • любая правка текста или клавиатуры меняет серверный keyboardRevision.
curl -s -X PATCH $CALAB/api/messages/$MSG \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"content": "Отчёт (исправлено)"}'
curl -s -X DELETE $CALAB/api/messages/$MSG -H "Authorization: Bearer $TOKEN"   # → 204

Пересылка

POST /api/rooms/{id}/messages/{mid}/forward {toRoomId} создаёт в целевой комнате копию с полем forward (откуда и чьё сообщение). У пересланных копий inline-кнопки не активны.

curl -s -X POST $CALAB/api/rooms/$ROOM/messages/$MSG/forward \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d "{\"toRoomId\": \"$OTHER_ROOM\"}"

Реакции

Emoji передаётся в пути в URL-кодировке. Ответ — 204. Один участник ставит на одно сообщение не больше 3 разных реакций (409 CONFLICT, reason: "REACTION_LIMIT"); всего на сообщении — до 20 разных emoji. В комнате — до 50 закреплённых сообщений.

curl -s -X PUT "$CALAB/api/messages/$MSG/reactions/%F0%9F%91%8D" -H "Authorization: Bearer $TOKEN"   # 👍 → 204
curl -s "$CALAB/api/messages/$MSG/reactions/%F0%9F%91%8D?limit=50" -H "Authorization: Bearer $TOKEN"
# {"users": [{"id": "…", "displayName": "Анна", …}], "hasMore": false}

История и одно сообщение

GET /api/rooms/{id}/messages возвращает {messages, hasMore}, новые сверху. Листайте назад через before=<id самого старого>, вперёд — через after=.

GET /api/rooms/{id}/messages/{messageId} возвращает 200 с голым Message — без обёртки {message: …}. Поля и подгрузка деталей те же, что в истории: id, roomId, authorId, content, replyToId, attachments, reactions (me — относительно вызывающего), метки времени, kind, system, sticker, forward. Поле command не заполнено — как во всех ответах REST.

const { messages, hasMore } = await bot.messages(roomId, { limit: 50 });
const one = await bot.message(roomId, messageId);

404 NOT_FOUND означает одно из: комната недоступна, сообщения нет или оно удалено, сообщение из другой комнаты, сообщение до отметки «очищенной» истории личных сообщений у вызывающего. Второй участник личных сообщений сохраняет свою историю. Для чтения не нужны ни SEND_MESSAGES, ни подключение к голосу.

Системные сообщения

Карточки, которые создаёт сервер, приходят с kind: "MESSAGE_KIND_SYSTEM" и полем system:

system.…Что это
recording {recordingId}Карточка записи встречи: используйте recordingId в URL расшифровки
achievement {achievementId, grantId, note, grantedBy}Выданное достижение; автор — получатель
automation {boardId, taskId, ruleId, ruleName, text}Сообщение правила автоматизации доски. text — простой текст без упоминаний и разметки, никого не уведомляет; authorId — создатель правила. Показывайте как «Автоматизация: ruleName»
roomIcon {fileId}Смена картинки комнаты (пусто — убрана); автор — кто сменил. Сама картинка — iconFileId комнаты

Команда-ответ на карточку несёт replyToId с id сообщения-карточки.

Отметка о прочтении

PUT /api/rooms/{id}/read {messageId} двигает отметку только вперёд. Бот не ставит людям отметку «прочитано» (✓✓) и сам не получает события о прочтении.

Расшифровка записи встречи

GET /api/rooms/{id}/recordings/{rid}/transcript возвращает 200 и всю сохранённую расшифровку без пагинации:

JSON
{
  "recordingId": "0192a100-0000-7000-8000-000000000001",
  "language": "ru",
  "segments": [
    {"speaker": 0, "startMs": 480, "endMs": 6900, "text": "Первый фрагмент."},
    {"speaker": -1, "startMs": 7200, "endMs": 12050, "text": "Неизвестный говорящий."}
  ]
}

startMs / endMs — числа в миллисекундах от начала записи; speaker — метка распознавания с нуля или -1, если говорящий неизвестен; language может быть пустым.

const t = await bot.transcript(roomId, recordingId);
for (const s of t.segments) console.log(s.speaker, s.text);

404 NOT_FOUND — комната недоступна, записи нет или она удалена, расшифровка ещё не готова, либо в комнате нет ни исходной записи, ни живой пересланной копии её карточки. Подставляйте roomId той карточки, которую видите, в том числе пересланной; удаление единственной пересланной копии отзывает доступ к расшифровке.

Подводные камни