Тема
Вебхуки
На цій сторінці описано два напрямки: події, які ми надсилаємо вам, і сигнал, яким ваша система будить агента.
У них різні схеми підпису
Це найлегше переплутати. Вихідні вебхуки підписують timestamp + "." + тіло. Вхідний сигнал тригера підписує тільки тіло.
Вихідні: події операторської роботи
Ми надсилаємо POST на вашу адресу при трьох подіях. Адреса вебхука задається в конфігурації агента.
| Подія | Коли | Що всередині |
|---|---|---|
escalated | розмову передано людині | transcript — недавній контекст |
visitor_message | відвідувач написав у переданій розмові | text — його репліка |
resolved | звернення закрито | — |
json
{
"event": "escalated",
"bot_id": "bot_7f3a1c",
"session_id": "3f6c1b9e-7f42-4d2a-9a6f-0d5b1f0b21c7",
"transcript": "Клієнт: Замовлення №4417 досі не приїхало\nБот: За даними перевізника посилка в дорозі…\nКлієнт: Мені це вже казали тиждень тому. Покличте людину.",
"at": 1753600000123
}session_id — ключ до всього подальшого: саме з ним працює операторська частина API.
Подія resolved приходить і тоді, коли звернення закрили ви самі через API. Це навмисно: так дві операторські системи — кабінет і ваша — лишаються синхронними.
Підпис
Кожна доставка підписана двома заголовками:
X-YouSelfBot-Timestamp: 1753600000
X-YouSelfBot-Signature: sha256=<hex>Підпис — це HMAC-SHA256(секрет, "<timestamp>.<сире тіло>") у hex. Секрет агента — у його конфігурації (handoff_webhook_secret), починається з whsec_.
Часова мітка входить у підпис
Підписаний рядок — timestamp + "." + body. Це дозволяє відхиляти старі доставки: перехоплений запит не можна відтворити пізніше, навіть якщо підпис досі математично вірний.
python
import hashlib, hmac, time
def verify(raw_body: bytes, headers, secret: str, tolerance: int = 300) -> bool:
ts = headers.get("X-YouSelfBot-Timestamp", "")
got = headers.get("X-YouSelfBot-Signature", "").removeprefix("sha256=")
if not ts.isdigit() or abs(time.time() - int(ts)) > tolerance:
return False # застаріла доставка
mac = hmac.new(secret.encode(), ts.encode() + b"." + raw_body, hashlib.sha256)
return hmac.compare_digest(mac.hexdigest(), got)javascript
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(rawBody, headers, secret, tolerance = 300) {
const ts = headers['x-youselfbot-timestamp'] ?? '';
const got = (headers['x-youselfbot-signature'] ?? '').replace(/^sha256=/, '');
if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > tolerance) return false;
const want = createHmac('sha256', secret)
.update(ts + '.')
.update(rawBody)
.digest('hex');
const a = Buffer.from(want);
const b = Buffer.from(got);
return a.length === b.length && timingSafeEqual(a, b);
}Три правила, які легко порушити:
- Рахуйте HMAC над сирим тілом, до JSON-парсингу. Переформатований JSON дасть інший підпис.
- Порівнюйте у сталий час (
compare_digest,timingSafeEqual), а не через==. - Відхиляйте старі доставки — кілька хвилин допуску достатньо.
Повтори
Невдалу доставку ми повторюємо тричі — через 1, 5 і 25 секунд. Повторюються лише 5xx і мережеві помилки: 4xx — це ваша система каже, що запит неправильний, і повторювати його немає сенсу.
Звідси два наслідки:
- Обробник має бути ідемпотентним — та сама подія може прийти двічі.
- Відповідайте
200одразу, а роботу робіть після. На доставку відводиться 5 секунд.
Адреса вебхука має бути публічною: запити у приватні мережі ми не робимо навмисно.
Вхідний сигнал тригера
Зворотний напрямок: ваша система будить агента, коли у вас щось сталося.
Тригер створюється в кабінеті — агент → Тригери → Додати тригер → За сигналом ззовні. Одразу після створення кабінет один раз покаже посилання й секрет. Див. Щоб працював без вас.
POST https://api.youselfbot.com/v1/hooks/{tid}Підпис
Автентифікація — HMAC-підпис тіла, а не ключ у заголовку:
X-Hook-Signature: sha256=<hex HMAC-SHA256(секрет, тіло)>Підписується тільки тіло, без часової мітки — на відміну від вихідних вебхуків вище.
bash
BODY='{"order_id":4417,"status":"delayed"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$HOOK_SECRET" -hex | awk '{print $2}')
curl -sS -X POST "https://api.youselfbot.com/v1/hooks/trg_01j9x2h7q0" \
-H "X-Hook-Signature: sha256=$SIG" \
-H "Content-Type: application/json" \
-d "$BODY"python
import hashlib, hmac, json, requests
body = json.dumps({"order_id": 4417, "status": "delayed"}).encode()
sig = hmac.new(SECRET.encode(), body, hashlib.sha256).hexdigest()
requests.post(
"https://api.youselfbot.com/v1/hooks/trg_01j9x2h7q0",
data=body,
headers={"Content-Type": "application/json", "X-Hook-Signature": "sha256=" + sig},
)Найчастіша помилка — порахувати HMAC над переформатованим JSON. Підписуйте ті самі байти, які надсилаєте.
Сам ідентифікатор тригера в адресі непередбачуваний, але секретом не вважається: адреса осідає в чужих конфігураціях і логах. Тому без валідного підпису — завжди 401.
Дві поведінки відповіді
Задаються в кабінеті, у формі тригера.
«Підтвердити одразу» — 202 Accepted одразу, робота йде у фоні:
json
{ "status": "accepted" }«Дочекатися відповіді агента» — з'єднання тримається до кінця роботи:
json
{
"run_id": "run_01j9x2m4p8",
"status": "done",
"answer": "Клієнта попереджено, доставку перенесено на завтра."
}Перший варіант правильний майже завжди: чужі системи часто мають короткий таймаут, а робота агента може тривати хвилини. Другий беріть, лише коли відповідь потрібна викликачу негайно, — і закладайте власний таймаут на своєму боці.
Якщо синхронний запуск упав — 502 з run_id, щоб знайти його в журналі Запуски.
Що бачить агент
Вхід запуску — це Задача запуску з тригера плюс сире тіло сигналу:
Обробі подію із CRM і, якщо потрібно, напиши мені.
Подія (тіло вебхука):
{"order_id":4417,"status":"delayed"}Тобто в тригері ви пишете, що робити, а сигнал приносить, з чим.
Обмеження
| Розмір тіла | до 256 КіБ |
| Вимкнений тригер | 409 |
| Невідомий або видалений тригер | 404 |
| Невірний підпис | 401 |
Дедуплікації немає
Повторна доставка тієї самої події зробить другий запуск. Якщо ваше джерело може доставити подію двічі, тримайте ознаку обробленого в позначках агента — розділ Стан, див. Позначки між запусками.
П'ять невдалих запусків поспіль ставлять тригер на паузу
Це захист від зламаної задачі, яка витрачала б кредити цілу ніч. Тригер треба буде ввімкнути вручну — див. Тригер на паузі.
Далі
Для розробника — виклики розмови, операторської роботи та знань.