Вебхуки
Вебхуки — простейший способ постить сообщения в каналы Lolka. Тебе не нужен бот и не нужно получать токен OAuth: достаточно создать вебхук в настройках канала и отправлять HTTP POST на секретный URL.
Создание вебхука
Вебхук принадлежит конкретному текстовому каналу внутри сервера. Создавать и редактировать вебхуки может любой участник, у которого есть право Управление вебхуками на этом канале.
- Открой настройки канала → вкладка «Интеграции» → «Вебхуки».
- Нажми «Новый вебхук», задай имя (1–80 символов) и, при желании, аватар.
- Скопируй URL — его и нужно будет использовать для отправки сообщений.
Также можно управлять вебхуками на уровне сервера (страница «Интеграции сервера»): там видно все вебхуки по всем каналам, куда у тебя есть доступ.
Объект вебхука
Когда вебхук отдаётся наружу (например, при создании), он имеет такую структуру:
| Поле | Тип | Описание |
|---|---|---|
id | snowflake | идентификатор вебхука |
type | integer | всегда 1 (Incoming) |
channel_id | snowflake | канал, в который постит вебхук |
server_id | snowflake | сервер, которому принадлежит канал |
name | string | имя по умолчанию (1–80 символов) |
avatar_url | ?string | URL аватарки, которая выводится у сообщений |
token | ?string | секрет вебхука; возвращается только при создании, регенерации и при запросе с правом Управление вебхуками |
url | ?string | полный URL вида /api/webhooks/{id}/{token}; возвращается вместе с токеном |
user | user object | краткий профиль пользователя, создавшего вебхук |
Пример
{
"id": 7234876123234,
"type": 1,
"channel_id": 881723489234,
"server_id": 771223489001,
"name": "Captain Hook",
"avatar_url": "https://cdn.lolka.app/avatars/webhook/..../256.webp",
"token": "NzIzNDg3NjEyMzIzNA.q8F2C...",
"url": "https://lolka.app/api/webhooks/7234876123234/NzIzNDg3NjEyMzIzNA.q8F2C...",
"user": {
"id": 100000000001,
"username": "nikita",
"display_name": "Никита",
"avatar_url": "https://cdn.lolka.app/avatars/user/..../128.webp"
}
}Отправка сообщения
Постит сообщение в канал вебхука. Аутентификация — самим token в URL; ни сессионный cookie, ни Authorization header не нужны.
Query-параметры
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
wait | boolean | true | если true — в ответе придёт созданное сообщение (200); если false — 204 без тела |
Тело запроса (JSON)
| Поле | Тип | Описание |
|---|---|---|
content | string | текст сообщения |
username | string | переопределяет имя вебхука для этого сообщения (1–80 символов после нормализации) |
avatar_url | string | HTTPS-URL на картинку, которая будет подставлена как аватар автора сообщения. Картинку мы скачаем, перекодируем в WebP и закешируем |
embeds | array of embed | до 10 rich-embed объектов — см. раздел Rich-embeds |
content, embeds или файлов (см. ниже) должно быть задано — пустое сообщение отправить нельзя.Пример
curl -X POST \
"https://lolka.app/api/webhooks/7234876123234/NzIzNDg3NjEyMzIzNA.q8F2C..." \
-H "Content-Type: application/json" \
-d '{
"content": "Привет из CI! Сборка #123 прошла :)",
"username": "build-bot",
"avatar_url": "https://example.com/bot.png"
}'Ответ
При wait=true (по умолчанию) в ответ приходит созданное сообщение в стандартном формате канальных сообщений:
{
"id": 98234876234,
"channel_id": 881723489234,
"author": {
"id": 7234876123234,
"username": "build-bot",
"is_webhook": true,
"webhook_id": 7234876123234
},
"content": "Привет из CI! Сборка #123 прошла :)",
"created_at": "2026-04-22T12:00:03Z"
}Отправка файлов
Чтобы прикрепить файлы, отправляй запрос как multipart/form-data. Текстовые параметры передавай как обычные поля формы, а файлы — полями files[0], files[1] и т.д.
- Максимум 10 файлов за один запрос.
- До 10 MB на файл.
- Поддерживаемые типы:
image/*,video/*,audio/*,application/pdf,text/plain.
curl -X POST \
"https://lolka.app/api/webhooks/7234876123234/NzIzNDg3NjEyMzIzNA.q8F2C..." \
-F "content=Отчёт за день во вложении" \
-F "files[0]=@./report.pdf"Rich-embeds
Rich-embed — это блок внутри сообщения с заголовком, описанием, картинками, цветной полосой, полями и футером. Передаётся как элемент массива embeds в теле запроса. Все поля опциональные — достаточно, например, одного title.
Объект embed
| Поле | Тип | Описание |
|---|---|---|
title | string | заголовок embed, до 256 символов |
description | string | описание, до 4096 символов. Поддерживает markdown — жирный, курсив, зачёркнутый, inline-код, блочные цитаты, ссылки |
url | string | HTTPS-ссылка; если задана, заголовок title становится кликабельным |
color | integer | целое число 0…0xFFFFFF — цвет полоски слева. Конвертируется из hex: #FF7A59 = 16742745 |
timestamp | ISO 8601 string | время, которое показывается в футере. Пример: "2026-04-22T18:00:00.000Z" |
author | object | блок автора (имя + иконка + ссылка) — см. ниже |
footer | object | нижний блок (текст + иконка) |
image | object | большая картинка снизу |
thumbnail | object | маленькая картинка справа |
fields | array of field | до 25 дополнительных полей «название/значение» |
Вложенные объекты
embed.author
| Поле | Тип | Описание |
|---|---|---|
name | string | имя автора, до 256 символов |
url | string | HTTPS-ссылка — делает name кликабельным |
icon_url | string | HTTPS-URL на иконку автора |
proxy_icon_url | string | CDN-URL — заполняется сервером |
embed.footer
| Поле | Тип | Описание |
|---|---|---|
text | string | текст футера, до 2048 символов |
icon_url | string | HTTPS-URL на иконку |
proxy_icon_url | string | CDN-URL — заполняется сервером |
embed.image / embed.thumbnail
| Поле | Тип | Описание |
|---|---|---|
url | string | HTTPS-URL на картинку — единственное поле, которое задаёт клиент |
proxy_url | string | CDN-URL — заполняется сервером |
width | integer | ширина — заполняется сервером |
height | integer | высота — заполняется сервером |
content_type | string | MIME-тип — заполняется сервером |
embed.fields[]
| Поле | Тип | Описание |
|---|---|---|
name | string | название поля, до 256 символов |
value | string | значение, до 1024 символов; поддерживает markdown |
inline | boolean | если true — поле встаёт в ряд с соседними inline-полями (до 3 в ряд) |
Пример
Полный payload с embed:
{
"content": "Новый релиз выкатили 🚀",
"embeds": [{
"title": "build #1247 · production",
"description": "**Успешно** задеплоено в прод.\n\n> Все smoke-тесты зелёные",
"url": "https://ci.example.com/builds/1247",
"color": 3066993,
"timestamp": "2026-04-22T18:00:00.000Z",
"author": {
"name": "ci-bot",
"url": "https://ci.example.com",
"icon_url": "https://ci.example.com/logo.png"
},
"footer": {
"text": "env: prod · v1.2.3",
"icon_url": "https://ci.example.com/favicon.png"
},
"thumbnail": {
"url": "https://ci.example.com/status-ok.png"
},
"image": {
"url": "https://ci.example.com/builds/1247/chart.png"
},
"fields": [
{ "name": "Ветка", "value": "main", "inline": true },
{ "name": "Коммит", "value": "`a1b2c3d`", "inline": true },
{ "name": "Длительность", "value": "4m 12s", "inline": true }
]
}]
}В ответе сервер вернёт embed обратно и дополнит поля картинок:
"embeds": [{
"type": "rich",
"title": "build #1247 · production",
"description": "**Успешно** задеплоено в прод.\n\n> Все smoke-тесты зелёные",
"url": "https://ci.example.com/builds/1247",
"color": 3066993,
"timestamp": "2026-04-22T18:00:00Z",
"author": {
"name": "ci-bot",
"url": "https://ci.example.com",
"icon_url": "https://ci.example.com/logo.png",
"proxy_icon_url": "https://cdn.lolka.app/embeds/a1b2c3d4....webp"
},
"footer": {
"text": "env: prod · v1.2.3",
"icon_url": "https://ci.example.com/favicon.png",
"proxy_icon_url": "https://cdn.lolka.app/embeds/e5f6g7h8....webp"
},
"thumbnail": {
"url": "https://ci.example.com/status-ok.png",
"proxy_url": "https://cdn.lolka.app/embeds/f0e1d2c3....webp",
"width": 200,
"height": 200
},
"image": {
"url": "https://ci.example.com/builds/1247/chart.png",
"proxy_url": "https://cdn.lolka.app/embeds/11223344....webp",
"width": 800,
"height": 420
},
"fields": [...]
}]content автоматически не добавляется — чтобы не дублировать блок.Лимиты embed
| Лимит | Значение |
|---|---|
| Embed на одно сообщение | до 10 |
| title | до 256 символов |
| description | до 4096 символов |
| author.name | до 256 символов |
| footer.text | до 2048 символов |
| fields на один embed | до 25 |
| field.name | до 256 символов |
| field.value | до 1024 символов |
| Суммарно всех текстов во всех embed | до 6000 символов |
| URL (во всех полях) | только https:// |
| color | 0 … 0xFFFFFF (16 777 215) |
При нарушении любого лимита сервер отвечает 400 Bad Request с описанием ошибки.
Slack- и GitHub-совместимые эндпоинты
Чтобы не переписывать существующие интеграции со Slack и GitHub, Lolka принимает их нативные форматы и сама конвертирует их в сообщение канала.
Принимает payload Slack Incoming Webhook (text, attachments, blocks в базовом виде). Достаточно просто подставить URL Lolka-вебхука вместо slack-hooks.
Принимает payload GitHub-вебхука. Обязателен заголовок X-GitHub-Event (например push, pull_request, issues) — по нему мы выбираем шаблон сообщения.
Управление сообщениями вебхука
У каждого отправленного сообщения можно получить его текущее состояние, отредактировать текст или удалить его. Аутентификация такая же — token вебхука в URL.
Получить сообщение
Возвращает сообщение в том же формате, что и ответ на POST.
Отредактировать сообщение
Меняет содержимое сообщения. Все поля опциональные.
| Поле | Тип | Описание |
|---|---|---|
content | string | null | null или отсутствует — оставить как есть; строка — заменить |
embeds | array | null | null или отсутствует — оставить как есть; [] — очистить все embed; массив — заменить полностью. Формат элементов — тот же объект embed |
{
"content": "Исправленный текст",
"embeds": [{
"title": "Обновлено",
"description": "Статус изменился",
"color": 16711680
}]
}Удалить сообщение
При успехе возвращает 204 No Content.
Лимиты
| Лимит | Значение | Где применяется |
|---|---|---|
| Запросов на один вебхук | 30 в минуту | на каждый отдельный вебхук |
| Сообщений от вебхуков на канал | 60 в минуту | суммарно по всем вебхукам канала |
| Вебхуков на канал | 15 | максимальное количество одновременно существующих |
| Файлов за один запрос | 10 | multipart upload |
| Размер одного файла | 10 MB | любой тип |
При превышении лимитов возвращается 429 Too Many Requests с телом { "code": "WEBHOOK_RATE_LIMIT_EXCEEDED" }. Рекомендуется добавлять экспоненциальный backoff при ретраях.
Безопасность
- Токен в URL = полный доступ к вебхуку. Любой, у кого есть ссылка, сможет постить сообщения в канал. Не коммить URL в публичные репозитории, не логируй его, не передавай в client-side-коде браузера.
- Если токен утёк — регенерируй его в настройках канала. Старый URL сразу перестанет работать.
- Мы не подписываем запросы сервером и не отдаём ни
X-Signature, ниX-Timestamp— единственным секретом является сам токен. - Все внешние URL (
avatar_url,icon_url,image.urlи т.д.) должны быть поhttps://и доступны из интернета.