Перейти к основному содержанию
Разработчикам Вебхуки

Публикуйте сообщения через вебхук

Вебхук представляет собой URL, через который сообщения попадают в ваше сообщество. Отправьте на него JSON из чего угодно, что умеет делать HTTP-запросы: из CI, мониторинга, cron-задачи или скрипта, и сообщение появится в канале.

Что можно сделать

  • Публикуйте текст или карточкуОбычный текст или карточка с заголовком, цветом, markdown, полями и изображениями.
  • Прикрепляйте файлыДо пяти файлов на сообщение: логи, отчёты, скриншоты.
  • Добавляйте кнопкиСсылки или кнопки, которые меняют карточку или обращаются к вашему сервису.
  • Меняйте сообщение позжеВ ответе есть callback URL, чтобы обновить или удалить сообщение.

В приложении

$ curl -X POST "$MSSGS_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-H "X-Mssgs-Signature: sha256=$SIG" \
-d '{"message_container": {"title": "Сборка #1847 прошла успешно", ...}}'
{"success": true, "message_id": "aZZ1a2b-..."}

Один запрос из CI, одна карточка в #deploys. Имя вверху совпадает с именем, которое вы дали вебхуку.

Быстрый старт

  1. Создайте вебхук

    В настольном приложении откройте в своём сообществе Управление сервером → Вебхуки, создайте вебхук, выберите каналы, в которые он может публиковать, и скопируйте URL для нужного канала. Он выглядит так:

    url
    https://mss.gs/api/v1/webhook/{webhook_guid}/{token}/{channel_guid}
  2. Отправьте сообщение

    Подпишите JSON секретом вебхука и отправьте его методом POST. У вебхука, созданного в настольном приложении, секрет есть всегда: скопируйте его из поля Секрет вебхука в настройках вебхука.

    bash
    BODY='{"content": "Build #1847 passed on main"}'
    SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$MSSGS_WEBHOOK_SECRET" | sed 's/^.* //')
    
    curl -X POST "$MSSGS_WEBHOOK_URL" \
      -H "Content-Type: application/json" \
      -H "X-Mssgs-Signature: sha256=$SIG" \
      -d "$BODY"
  3. Прочитайте ответ

    В ответе есть id сообщения и callback_url, чтобы изменить сообщение позже.

    json
    {
      "success": true,
      "message_id": "aZZ1a2b-...",
      "callback_url": "https://mss.gs/api/v1/trigger-callback/8f14e45f-...",
      "stream_url": "https://mss.gs/api/v1/instant/6b1e...c0?token=a1b2c3d4-..."
    }
Любой, у кого есть этот URL и секрет, может публиковать в канал. Не храните их в публичных репозиториях и клиентском коде.

Что можно отправить

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

Короткая форма

Этого хватает для большинства оповещений.

json
{
  "content": "Server backup completed at 03:00 UTC",
  "color": "green",
  "title": "Backup Bot"
}
ПолеТипЧто делает
contentstringТекст сообщения. Обязателен, если вы не отправляете карточку или файлы.
colorstringblue (по умолчанию), green, orange, red, yellow или purple.
titlestringЗаголовок над текстом. По умолчанию имя вебхука.

Полная карточка

Отправьте message_container, чтобы получить карточку с заголовком-ссылкой, подзаголовком, markdown, полями и изображениями. Через вебхук карточка принимает type, color, title, title_url, sub_title, description, fields, avatar_url, image_url, image_base64, images и поля индикатора загрузки. Плашка статуса, бейдж, статистика diff и свёрнутые размышления доступны только в ответах на команды. Все поля описаны на странице карточек сообщений.

json
{
  "message_container": {
    "type": "embed_message",
    "color": "green",
    "title": "Build #1847 passed",
    "title_url": "https://ci.example.com/builds/1847",
    "description": "All 212 tests green on **main**.",
    "fields": [
      { "field": "Duration", "value": "2m 34s" },
      { "field": "Commit", "value": "1a2b3c4" }
    ]
  }
}
deploys
System
Сообщение от CI

Сборка #1847 прошла успешно

Все 212 тестов на main зелёные.
Duration
2m 34s
Commit
1a2b3c4

Кнопки

Добавьте массив actions, чтобы разместить кнопки под сообщением. Как они работают, описано на странице о кнопках.

Файлы

Публикуйте вместе с сообщением настоящие файлы: лог, отчёт, скриншот. Они отображаются как любые другие вложения: строкой для скачивания, а изображения, видео и аудио прямо в сообщении. Сообщение только с файлами тоже допустимо: просто не передавайте content и карточку.

json
{
  "message_container": {
    "color": "orange",
    "title": "Log dump: ios",
    "description": "DMs stopped arriving after switching networks"
  },
  "attachments": [
    {
      "name": "mssgs-logs-20260803-141205.log",
      "content_base64": "MjAyNi0wOC0wMyAxNDoxMjowNSBbV1NdIGNvbm5lY3RlZAo=",
      "mime_type": "text/plain"
    }
  ]
}
ПолеТипЧто делает
namestringИмя, под которым файл скачивается. Обязательно. От пути остаётся только последняя часть.
content_base64stringБайты файла в base64, простой строкой или как data: URI. mssgs сохраняет файл и оставляет в сообщении только ссылку.
mime_typestringТип содержимого content_base64. По умолчанию text/plain.
urlstringФайл, уже размещённый на mssgs: путь /static/... или URL https://mss.gs/....
Для каждого файла передавайте ровно одно из полей: content_base64 или url. Оба сразу или ни одного считается ошибкой.
ЛимитЗначение
Файлов на сообщение5
Размер файла после декодирования8 МБ
Имя файла200 символов
Весь запросОколо 10 МБ. Base64 увеличивает файл на треть, поэтому один файл больше примерно 7 МБ не поместится.

Почему url принимает только адреса mssgs

URL вебхука часто оказывается вставленным в чужие дашборды. Если он утечёт, это не должно позволить кому-то заставить приложение каждого участника загрузить файл с выбранного им сервера. Если ваш файл лежит в другом месте, отправьте его как content_base64, и mssgs разместит его у себя.

Неудачная загрузка не отменяет сообщение

Файлы проверяются сразу, а загружаются позже. Если загрузка не удалась, этот файл пропускается, а остальное сообщение всё равно публикуется, без ошибки: лучше потерять файл, чем весь отчёт. Если файл важен, проверьте, что он дошёл.

Файл из командной строки

bash
BODY='{"attachments":[{"name":"report.csv","content_base64":"'"$(base64 < report.csv | tr -d '\n')"'","mime_type":"text/csv"}]}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$MSSGS_WEBHOOK_SECRET" | sed 's/^.* //')

printf '%s' "$BODY" | curl -sS -X POST "$MSSGS_WEBHOOK_URL" \
  -H 'Content-Type: application/json' \
  -H "X-Mssgs-Signature: sha256=$SIG" \
  --data-binary @-

Подпись запросов

Вебхук с секретом принимает только те запросы, которые доказывают, что знают его, а у вебхука, созданного в настольном приложении, секрет есть всегда (Секрет вебхука в его настройках). Подпишите необработанное тело запроса с помощью HMAC-SHA256 этим секретом и отправьте шестнадцатеричный дайджест в нижнем регистре в заголовке X-Mssgs-Signature в виде sha256=<hex>.

javascript
import crypto from 'node:crypto';

const body = JSON.stringify({ content: 'Deploy finished' });
const signature = crypto.createHmac('sha256', process.env.MSSGS_WEBHOOK_SECRET)
  .update(body)
  .digest('hex');

await fetch(process.env.MSSGS_WEBHOOK_URL, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Mssgs-Signature': `sha256=${signature}`
  },
  body
});
Запрос без действительной подписи получает 401 и {"error": "INVALID_SIGNATURE"}. Неподписанные запросы принимает только вебхук без секрета, например созданный через MCP без webhook_secret.

Собственный заголовок GitHub X-Hub-Signature-256 тоже принимается, так что вебхук GitHub с тем же секретом работает без изменений. В подписи нет метки времени, поэтому она не мешает повторно отправить перехваченный запрос: главным секретом остаётся сам URL.

Ответы и ошибки

Опубликованное сообщение возвращается с id и callback_url, через который его можно обновить или удалить в течение 30 минут.

json
{
  "success": true,
  "message_id": "aZZ1a2b-...",
  "callback_url": "https://mss.gs/api/v1/trigger-callback/8f14e45f-...",
  "stream_url": "https://mss.gs/api/v1/instant/6b1e...c0?token=a1b2c3d4-..."
}
Проверяйте не только статус, но и тело ответа. Отклонённый payload сообщает причину кодом error в теле. Считайте неудачей любое тело с ключом error, каким бы ни был код статуса.
http
HTTP/1.1 200 OK
Content-Type: application/json

{ "error": "ATTACHMENT_TOO_LARGE" }
javascript
const res = await fetch(webhookUrl, { method: 'POST', headers, body });
const reply = await res.json().catch(() => null);

// A rejected payload still comes back as 200: read the body.
if (!res.ok || (reply && reply.error)) {
  throw new Error(`webhook rejected: ${reply?.error ?? res.status}`);
}

Если у сообщения есть кнопки, в ответе также есть stream_url: живой поток ответов, реакций и нажатий кнопок на этом сообщении. Он открыт 10 минут или час, если отправить "sse_event_extended_timeout": true. Подробнее на странице обновлений вживую.

Коды статуса

СтатусКогда
401У вебхука есть секрет, а подпись отсутствует или неверна.
403Этому вебхуку нельзя публиковать в этот канал.
404По этому URL нет вебхука.
413Запрос слишком большой.
429Слишком много запросов. Сбавьте темп и повторите попытку.
502Сообщение не удалось доставить. Повторите попытку.

Коды ошибок

КодЧто означает
MISSING_CONTENTНечего публиковать: нет ни текста, ни карточки, ни файлов.
INVALID_MESSAGE_CONTAINERmessage_container не является объектом.
INVALID_MESSAGE_CONTAINER_TYPEТип карточки не embed_message и не system_message.
MISSING_MESSAGE_CONTAINER_DESCRIPTIONКарточке нужно описание, если это не индикатор загрузки.
MISSING_MESSAGE_CONTAINER_LOADER_TEXTКарточке с индикатором загрузки нужен loader_text.
INVALID_WEBHOOK_BINDINGЭтому вебхуку нельзя публиковать в этот канал.
INVALID_SIGNATUREЗаголовок подписи отсутствует или неверен.
REQUEST_BODY_TOO_LARGEЗапрос превышает лимит размера.
PUBLISH_FAILEDСообщение не удалось доставить.
INVALID_ACTIONS_FORMAT, INVALID_ACTION_MISSING_FIELDS, DUPLICATE_ACTION_ID, INVALID_TRIGGERS_FORMAT, INVALID_TRIGGER_MISSING_ACTION, INVALID_TRIGGER_ACTION_NOT_ALLOWEDЧто-то не так с кнопкой. Подробнее на странице о кнопках.

Ошибки файлов

КодЧто означает
INVALID_ATTACHMENTS_FORMATattachments не является списком, или один из элементов не является объектом.
TOO_MANY_ATTACHMENTSБольше пяти файлов.
MISSING_ATTACHMENT_NAMEУ файла нет имени.
INVALID_ATTACHMENT_NAMEОт имени не остаётся ничего пригодного, например ...
MISSING_ATTACHMENT_SOURCEНет ни url, ни content_base64.
AMBIGUOUS_ATTACHMENT_SOURCEПереданы и url, и content_base64.
INVALID_ATTACHMENT_BASE64Base64 не декодируется.
ATTACHMENT_TOO_LARGEФайл больше 8 МБ после декодирования.
INVALID_ATTACHMENT_URLurl не является адресом mssgs.

Лимиты

ЛимитЗначение
Размер запросаОколо 10 МБ
Файлов на сообщение5, каждый до 8 МБ
Описание карточкиДо 50 000 байт. Если оно длиннее 1 000 байт, участники видят начало и кнопку Показать больше.
Изменение сообщения после публикации30 минут, через callback_url
Живой поток сообщения с кнопками10 минут или час по запросу

Число запросов ограничено. Получив 429, подождите, прежде чем отправлять снова, а оповещения, которые приходят пачками, объединяйте в одно сообщение.

GitHub, UniFi и App Store Connect

Направьте один из этих сервисов на URL вебхука, и mssgs распознает его и опубликует полноценную карточку, без payload, который нужно писать самому. См. интеграции. На такие запросы приходит ответ {"success": true} без callback URL.

ИсточникКак распознаётсяЧто публикует
GitHubЗаголовок x-github-eventПуши, pull request и ревью, issues и комментарии, ветки и теги, релизы. Серия изменений в одном issue или pull request собирается в одну карточку.
UniFi ProtectUser agent protect-alarm-managerЗвонки в дверь, движение, а также люди, машины или посылки, которые заметили ваши камеры.
App Store ConnectТело уведомления или заголовок x-apple-signatureУведомления App Store Connect.

Что дальше