Вебхуки

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

Где управлять вебхуками

Откройте свой аватар в правом верхнем углу, затем выберите Интеграции. На страницах интеграций текущего сообщества перечислены Входящие вебхуки и Исходящие вебхуки. Каждый вебхук принадлежит одному сообществу.

По умолчанию вебхуки могут создавать администраторы сообщества и системные администраторы. Системный администратор может предоставить это право другим ролям в настройках разрешений Системной консоли. Системный администратор также может отключить каждый тип вебхука в Системная консоль › Интеграции › Управление интеграцией: Разрешить входящие вебхуки и Разрешить исходящие вебхуки включены по умолчанию.

A channel with messages from a CI bot and from GitHub, shown as cards with fields, pipeline stages and a button
Сообщения интеграций, отображаемые в виде карточек: поля, этапы конвейера и кнопка действия.

Создание входящего вебхука

  1. В разделе Интеграции выберите Входящие вебхуки, затем Добавить входящий Webhook.
  2. Заполните Заголовок и Описание: они отображаются только на страницах интеграций.
  3. Выберите Канал, в который сообщения отправляются по умолчанию. Для закрытого канала вы должны быть его участником.
  4. Отметьте Прикрепить к этому каналу, если отправители не должны иметь возможность публиковать сообщения в других каналах.
  5. При необходимости задайте Имя пользователя и URL Изображения профиля для его сообщений (см. Имя и значок).
  6. Выберите Сохранить. Скопируйте отображаемый URL: https://chat.example.com/hooks/<key>.
URL — единственный секрет: любой, у кого он есть, может публиковать сообщения в канале. Не публикуйте его в открытых репозиториях и удалите вебхук, если он утечет.

Через API: POST /api/v4/hooks/incoming с {"channel_id", "display_name", "description", "username", "icon_url", "channel_locked"}; id в ответе является ключом. Из командной строки: amctl webhook create-incoming --channel <channel id> --user <user> --display-name "CI".

Отправка сообщения во входящий вебхук

curl -X POST https://chat.example.com/hooks/xgh5ycrx1jf7mbs9w8z4c8e7ho \
  -H 'Content-Type: application/json' \
  -d '{"text": "Deploy of **api 2.4.0** finished :white_check_mark:"}'

Сервер отвечает 200 с телом ok. Он также принимает application/x-www-form-urlencoded с JSON в поле payload, как вебхуки Slack, поэтому инструменты, созданные для Slack, обычно работают без изменений.

ПолеЗначение
textСообщение в формате Markdown. Обязательное поле, если нет attachments. Текст, превышающий лимит длины сообщения сервера, разбивается на несколько сообщений.
channelДругой канал сообщества вебхука, по имени (ops-alerts или #ops-alerts), или @username для личного сообщения от владельца вебхука. Отказывается, если вебхук привязан к своему каналу. Старое имя town-square по-прежнему ведёт в канал по умолчанию сообщества, который теперь называется general.
username, icon_url, icon_emojiИмя и изображение для отображения, если сервер разрешает переопределения.
attachmentsВложения к сообщению, отображаемые в виде карточек (см. ниже).
propsДополнительные свойства, сохраняемые в сообщении для приложений, которые их читают.
typeПользовательский тип сообщения, например custom_poll, для приложения, которое его отображает. Типы, начинающиеся с system_, отклоняются.
root_idID первого сообщения Обсуждения, чтобы ответить в этом Обсуждении.
priorityПриоритет сообщения, например {"priority": "urgent"}.
silenttrue отправляет сообщение без push-уведомлений.

Каждое сообщение, отправленное вебхуком, имеет метку БОТ рядом с именем, даже если оно отображается от имени создателя, чтобы его можно было отличить от сообщения реального человека. Сообщения вебхуков никогда не активируют исходящие вебхуки.

Имя и значок

Вебхук публикует сообщения от имени человека, который его создал, если сервер не разрешает переопределение. Два параметра в Системная консоль › Интеграции › Управление интеграцией, оба отключены по умолчанию, изменяют это поведение:

  • Разрешить интеграциям переопределять имена пользователей (ServiceSettings.EnablePostUsernameOverride): отображается Имя пользователя вебхука или username из запроса; если ни одно из них не задано, имя будет webhook.
  • Разрешить интеграциям переопеределять иконки изображений профилей (ServiceSettings.EnablePostIconOverride): отображается Изображение профиля вебхука или icon_url или icon_emoji из запроса.

Значение из запроса имеет приоритет над сохранённым в вебхуке.

Вложения

Вложения добавляют структурированный контент под текстом. Они следуют формату вложений сообщений Slack:

{
  "text": "Build failed on `main`",
  "attachments": [{
    "fallback": "main #1902 failed",
    "color": "#d24b4e",
    "author_name": "GitHub Actions",
    "author_icon": "https://chat.example.com/static/ci.png",
    "title": "main #1902 failed",
    "title_link": "https://ci.example.com/runs/1902",
    "text": "Commit `0f4d8e2` by Jonas Weber",
    "fields": [
      {"title": "Branch", "value": "zephyr-3.7", "short": true},
      {"title": "Duration", "value": "3m 40s", "short": true}
    ],
    "footer": "Lumen CI",
    "ts": 1760000000
  }]
}
ПолеЗначение
fallbackОбычный текст для мест, где карточка не может быть отображена, например в уведомлениях.
colorЦветная полоса: шестнадцатеричный цвет, либо good, warning, danger.
pretextТекст над карточкой.
author_name, author_link, author_iconНебольшая строка заголовка.
title, title_linkЗаголовок карточки, опционально со ссылкой.
textТело карточки, в формате Markdown.
fieldsСписок {"title", "value", "short"}; поля short располагаются рядом.
image_url, thumb_urlБольшое изображение внизу или маленькое сбоку.
footer, footer_icon, tsСтрока подвала и время (в секундах Unix).
actionsКнопки и меню: см. Интерактивные сообщения.

Карточки: этапы и графики

Antimatter добавляет к вложениям два поля, отображаемые стандартным интерфейсом:

  • stages: шаги конвейера в виде «пилюль», до 20. Каждый элемент имеет вид {"name", "status"}, где status принимает значение passed, failed, running, pending, skipped или canceled.
  • chart: небольшой линейный график последних значений, {"values": [2 to 200 numbers], "threshold": number, "label": "…"}. Порог отображается пунктирной линией, например, для ограничения тревоги.
{
  "attachments": [{
    "color": "danger",
    "title": "main #1902 failed",
    "stages": [
      {"name": "build", "status": "failed"},
      {"name": "unit", "status": "skipped"},
      {"name": "publish", "status": "skipped"}
    ]
  }, {
    "color": "warning",
    "title": "API latency above 400 ms",
    "chart": {"values": [210, 230, 260, 390, 450, 470], "threshold": 400, "label": "p95 latency, ms"}
  }]
}

Классический интерфейс и другие клиенты игнорируют эти два поля, поэтому дублируйте основную информацию в полях text или fields.

Создание исходящего вебхука

Исходящий вебхук отслеживает общие каналы сообщества. Он отправляет сообщение на ваш URL, когда первое слово сообщения совпадает с одним из его триггерных слов, либо когда кто-то выбирает его в меню сообщения (действие с сообщением).

  1. В разделе Интеграции выберите Исходящие вебхуки, затем Добавить исходящий Webhook.
  2. Заполните Заголовок и Описание.
  3. Выберите Тип содержимого запроса: application/x-www-form-urlencoded или application/json.
  4. Выберите Канал, введите Ключевые слова (по одному на строку) или и то, и другое. Если указан канал, но нет ключевых слов, отправляются все сообщения этого канала. Если указаны оба условия, сообщение должно соответствовать обоим.
  5. В разделе Условие срабатывания выберите Первое слово соответствует слову события полностью, Первое слово начинается со слова триггера или Из меню сообщения. При выборе Из меню сообщения слова триггера исчезают, Канал становится необязательным (без него действие предлагается во всех публичных каналах сообщества), а Заголовок становится обязательным: это имя действия в меню.
  6. Введите URL обратного вызова (по одному на строку). Каждому URL отправляется каждое подходящее сообщение.
  7. При необходимости задайте Имя пользователя и Изображение профиля, используемые для ответов. Для действия из меню сообщения поле Значок заменяет Изображение профиля: это URL изображения в формате .png или .jpg или загруженное изображение в интерфейсе Antimatter по умолчанию. В меню оно отображается серым цветом; без него действие получает нейтральный значок.
  8. Нажмите Сохранить и скопируйте отображаемый токен. Ваша служба использует его для проверки того, что запросы исходят от вашего сервера.

Через API: POST /api/v4/hooks/outgoing с {"team_id", "channel_id", "trigger_words", "trigger_when", "callback_urls", "content_type", "display_name"} (trigger_when: 0 для точного совпадения, 1 для «начинается с», 2 для «из меню сообщения»). Вебхук с trigger_when 2 не принимает триггерные слова и требует display_name. Загрузите его иконку с помощью POST /api/v4/hooks/outgoing/{hook_id}/icon (multipart, поле image: PNG, JPEG или GIF, хранится как PNG 128 px); его icon_url затем указывает на GET /api/v4/hooks/outgoing/{hook_id}/icon.

Если ваш сервис находится в частной сети, системный администратор должен разрешить его адрес в Разрешить недоверенные внутренние соединения для (ServiceSettings.AllowedUntrustedInternalConnections). В противном случае сервер не обращается к частным адресам.

Исходящий запрос

Antimatter отправляет POST-запрос с этими полями в формате JSON или form-encoded в зависимости от типа содержимого:

{
  "token": "6kd1ofo5ojr5zgu5ipfipybgnc",
  "team_id": "…",
  "team_domain": "lumen-labs",
  "channel_id": "…",
  "channel_name": "ops-alerts",
  "timestamp": 1760010000000,
  "user_id": "…",
  "user_name": "priya",
  "post_id": "…",
  "text": "deploy api 2.4.0",
  "trigger_word": "deploy",
  "file_ids": ""
}

Сначала сравните token с токеном вебхука. В версии с кодировкой формы timestamp указывается в секундах, а не в миллисекундах. Сервер ожидает ваш ответ в течение времени, разрешённого параметром Таймаут запроса на интеграцию (по умолчанию 30 секунд, ServiceSettings.OutgoingIntegrationRequestsTimeout).

Ответ на исходящий вебхук

Чтобы отправить ответ, верните JSON. Чтобы ничего не отправлять, верните пустое тело ответа.

{
  "text": "Deploying **api 2.4.0** to staging…",
  "response_type": "comment",
  "username": "deploy-bot",
  "icon_url": "https://chat.example.com/static/deploy.png"
}
  • text и attachments: содержимое ответа. Если ни одно из полей не указано, ничего не будет отправлено.
  • response_type: значение comment отправляет ответ в обсуждение сообщения, вызвавшего вебхук; в противном случае ответ публикуется в канале.
  • username, icon_url: отображаются, если сервер разрешает переопределение; в противном случае используются собственные значения вебхука.
  • props, type, priority: как для входящих вебхуков.

Ответ публикуется от имени создателя вебхука, с переопределённым именем, если оно указано. Для ответов длиннее короткого ответа отправьте ответ сразу, а затем опубликуйте его позже с помощью входящего вебхука или API.

Действия с сообщением

Действие с сообщением — это исходящий вебхук с параметром Условие срабатывания, установленным в значение Из меню сообщения. Он никогда не срабатывает на отправленных сообщениях. Вместо этого пользователи видят его в меню сообщения: Действия с сообщением (⋯) › Другие действия, в разделе Интеграции, с его иконкой. В классическом интерфейсе действия с сообщением не отображаются. При выборе действия одно это сообщение отправляется на URL обратного вызова, а короткое уведомление показывает Отправлено в с именем действия или отображает ваш ephemeral_text.

  • Где отображается: в канале вебхука или, если канал не указан, во всех публичных каналах сообщества. Действия с сообщением доступны только в публичных каналах.
  • Гости получают только действия, привязанные к каналу, в котором они находятся, а не общесообщественные.
  • Архивированные и каналы только для чтения: сообщение всё равно может быть отправлено. Архивирование канала удаляет вебхуки, привязанные к нему, поэтому в архивированном канале остаются только общесообщественные действия. Ответ, который пользователь не мог отправить в этот канал, показывается только ему.
  • Ограничение частоты: каждый пользователь может выполнить 10 действий с сообщением в минуту после серии из 5. При превышении сервер отвечает 429.
  • Частные сети: как и для других исходящих вебхуков, внутренние адреса должны быть разрешены в параметре Разрешить недоверенные внутренние соединения для.

Запрос

Данные отправляются в формате JSON или form-encoded, в соответствии с типом содержимого вебхука:

{
  "token": "6kd1ofo5ojr5zgu5ipfipybgnc",
  "trigger": "message_action",
  "team_id": "…",
  "team_domain": "lumen-labs",
  "channel_id": "…",
  "channel_name": "ops-alerts",
  "timestamp": 1760010000000,
  "user_id": "…",
  "user_name": "priya",
  "post_id": "…",
  "post_root_id": "",
  "post_create_at": 1760009000000,
  "post_user_id": "…",
  "post_user_name": "jonas",
  "permalink": "https://chat.example.com/lumen-labs/pl/…",
  "text": "The staging database is full again",
  "files": [{"id": "…", "name": "df.txt", "link": "https://chat.example.com/api/v4/files/…"}],
  "priority": "",
  "requested_ack": false,
  "burn_on_read": false
}
  • user_id и user_name — это пользователь, выбравший действие; post_user_id и post_user_name — автор сообщения.
  • Отправляется только это сообщение, никогда — остальная часть обсуждения.
  • files содержит файлы сообщения только в том случае, если пользователь, выбравший действие, имеет право их скачать. Скачивание link требует сессии или токена, как и любой файл API.
  • Сообщение, сгорающее после прочтения, отправляется без текста и файлов, с параметром burn_on_read, установленным в true.
  • При кодировании формы файлы передаются как повторяющиеся поля file_id, file_name и file_link в том же порядке.

Ответ

Ответ формируется так же, как для любого исходящего вебхука (см. Ответ на исходящий вебхук): text или attachments публикует ответ в канале, а "response_type": "comment" — в обсуждении сообщения. Также можно ответить с помощью ephemeral_text (не более 300 символов): он отображается только тому, кто выбрал действие, в уведомлении. Пустое тело ответа ничего не публикует.

{"ephemeral_text": "Ticket OPS-412 created"}

С помощью API

  • GET /api/v4/channels/{channel_id}/message_actions: действия, доступные в канале, с полями id, display_name, description и icon_url. Токены и URL обратного вызова не возвращаются.
  • POST /api/v4/posts/{post_id}/message_actions/{hook_id}: выполняет действие над сообщением, которое вы можете прочитать. Возвращает {"ephemeral_text"}.

Если вебхук не работает

  • 501 от URL вебхука: входящие вебхуки отключены на сервере.
  • 400 "Failed to decode the payload…": JSON недействителен, или поле формы не называется payload. Сообщение "No text specified." означает, что в запросе нет ни text, ни attachments.
  • 403: вебхук привязан к другому каналу, или его владелец больше не может читать канал.
  • Исходящий вебхук никогда не срабатывает: канал закрытый, триггер не является первым словом, сообщение отправлено вебхуком или вебхук настроен на срабатывание Из меню сообщения.
  • Действие не отображается в меню: канал закрытый, действие привязано к другому каналу, пользователь является гостем, а действие не привязано к каналу, или исходящие вебхуки отключены.
  • 404 при выполнении действия: действие не существует или не распространяется на канал данного сообщения для этого пользователя. 429: пользователь выполнил более 10 действий за минуту. 502: ни один URL обратного вызова недоступен (причина указана в журнале).
  • Журналы: при включённом параметре Включить отладку Webhook-ов (LogSettings.EnableWebhookDebugging) и уровне журнала DEBUG сервер записывает в журнал данные (payloads) неудачных входящих запросов. Неудачные исходящие вызовы записываются как Outgoing Webhook POST failed.