Я направил ИИ-агента для кодинга на документацию вебхуков — и он собрал рабочего бота-автоответчика — UnifyPort
Знакомый ход. В списке дел висит интеграция с мессенджером, и вместо того чтобы читать документацию от корки до корки, вы открываете Cursor — или Claude Code, или Windsurf — и начинаете писать промпты. В 2026 году большинство из нас именно так и начинает проект. Настоящий вопрос не в том, будете ли вы «вайб-кодить» с ИИ. Он в том, даёт ли API, с которым вы работаете, агенту достаточно, чтобы чисто довести дело до конца.
Это билд-лог. К концу у вас будет рабочий бот-автоответчик: приёмник вебхуков, который проверяет подпись на каждом входящем сообщении и отвечает через единственный эндпойнт отправки — примерно день работы, и бóльшую часть делает агент. Быстро получается потому, что вебхук UnifyPort нормализован. Нужно выучить одну форму события и вызвать один эндпойнт — агенту нечего угадывать.
Подготовка: направьте агента на документацию
Самый важный шаг — тот, который все пропускают: занесите справочник API в контекст агента до того, как попросите код. Три рабочих подхода на сегодня:
- Вставьте страницу. Скопируйте форму события
message.receivedи раздел отправкиPOST /v1/messagesпрямо в чат. - Прикрепите документацию.
@Docsв Cursor, файловый контекст Claude Code, панель документации Windsurf — направьте их на справочник UnifyPort, чтобы агент читал его по запросу. - Используйте коннектор документации/MCP, если ваш инструмент его поддерживает, чтобы агент сам подтягивал справочник.
Отдача конкретна. Агент, который действительно прочитал имена событий, не выдумает обработчик onMessage() или несуществующий SDK sendText(). Он пишет под message.received и POST /v1/messages, потому что именно это у него перед глазами. Мусорный контекст на входе — галлюцинированные эндпойнты на выходе, так что потратьте эти две минуты и заведите настоящие.
Сборка, промпт за промптом
Первый промпт — приёмник:
По документации вебхуков UnifyPort, что я дал, напиши Express-сервер с маршрутом
POST /webhook, принимающим стандартные события. Пока что: когдаeventравенmessage.received, логируйfromиtext.
Поскольку форма события фиксирована, обработчик — это ветвление по одному полю:
import express from "express";
const app = express();
app.use(express.json());
app.post("/webhook", (req, res) => {
const evt = req.body;
if (evt.event === "message.received") {
console.log(`${evt.provider} ${evt.from}: ${evt.text}`);
}
res.sendStatus(200);
});
app.listen(3000, () => console.log("listening on :3000"));
Второй промпт — проверка подписи. Этот шаг небрежная сборка пропускает, а агент, прочитавший документацию, — нет:
Каждая доставка подписана HMAC-SHA256 с
signing_secretвебхука. Проверь её, прежде чем доверять телу. Используй сырое тело запроса и сравнение, устойчивое по времени.
Здесь агент обязан попасть в одно: HMAC должен считаться по сырым байтам, а не по повторно сериализованному JSON. С подсказкой «используй сырое тело запроса» хороший агент подключает хук verify у express.json, чтобы поймать rawBody. Если забудет — это ваша единственная точка ревью, и вы заметите её при первой же реальной доставке с ответом 401.
Третий промпт — ответ через эндпойнт отправки:
Когда приходит
message.received, ответь отправителю черезPOST /v1/messages, используяaccount_idиfromиз события. Авторизация — Bearer API-ключ из окружения.
Вот и весь бот. Полный файл, на котором останавливается агент:
import express from "express";
import crypto from "crypto";
const { UNIFYPORT_API_KEY, UNIFYPORT_SIGNING_SECRET } = process.env;
const app = express();
app.use(express.json({ verify: (req, _res, buf) => { req.rawBody = buf; } }));
function isValid(req) {
const sig = req.get("x-unifyport-signature") || "";
const expected = crypto
.createHmac("sha256", UNIFYPORT_SIGNING_SECRET)
.update(req.rawBody)
.digest("hex");
return sig.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}
app.post("/webhook", async (req, res) => {
if (!isValid(req)) return res.sendStatus(401);
const evt = req.body;
if (evt.event === "message.received") {
await fetch("https://api.unifyport.ai/v1/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${UNIFYPORT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
account_id: evt.account_id,
to: evt.from,
text: "Спасибо! Мы получили ваше сообщение — оператор скоро ответит.",
}),
});
}
res.sendStatus(200);
});
app.listen(3000, () => console.log("listening on :3000"));
Запустите и посмотрите, как приходит сообщение
Подключите один аккаунт (Telegram проще всего для теста), зарегистрируйте эндпойнт вебхука на ваш сервер с signing_secret и subscribed_events: ["message.received"], затем отправьте боту личное сообщение. Событие приходит таким:
{
"event": "message.received",
"account_id": "acct_8Q2vK",
"provider": "telegram",
"from": "user_3f9c1a",
"text": "Здравствуйте, вы работаете по выходным?",
"timestamp": 1749427200,
"message_id": "tg_msg_5d2b7e"
}
Ваш сервер проверяет подпись, вызывает POST /v1/messages, и ответ появляется в чате. Если проверка подписи не прошла, вы получите 401 — это та самая ошибка с rawBody сверху. Исправьте один раз, и цикл станет надёжным.
Расширьте
Вот где нормализованный вебхук окупается. Добавить вторую платформу — это не вторая интеграция, а ноль нового кода обработчика. Подключите аккаунт WhatsApp, LINE или Zalo, подпишите тот же вебхук — и сработает ровно та же ветка message.received, потому что форма события не меняется между провайдерами. Поле provider подскажет, откуда пришло, а остальной код на это даже не смотрит.
Хотите, чтобы бот действительно отвечал, а не просто подтверждал получение? Это ещё один промпт: «перед ответом отправь text в LLM и используй его ответ как тело сообщения». Вызов отправки агент уже написал — вы лишь меняете одну строку на ответ модели.
Урок этого дня не в том, что «ИИ пишет бота за вас». В том, что ИИ-агент хорош ровно настолько, насколько хороша поверхность, на которую вы его навели. Нормализованный вебхук со стабильными именами событий и одним эндпойнтом отправки — именно та поверхность, которую агент доводит до конца чисто: одна форма для изучения, ничего выдумывать не надо. Направьте инструмент на UnifyPort v1 API, вставьте форму message.received — и до конца дня у вас будет бот, который проверяет подпись и отвечает, а дальше отпустите его на остальные пять каналов.