Вебхуки
ReMeet отправляет на ваш сервер подписанные POST-запросы, когда что-то происходит. Настройка — в кабинете разработчика: ваше приложение → API и вебхуки → адрес (https) и события. При первом сохранении вы получите секрет подписи (показывается один раз).
События
| Событие | Когда | data |
|---|---|---|
app.installed | Человек установил приложение | user.id |
app.uninstalled | Человек удалил его | user.id |
meeting.started | Встреча началась | meeting |
meeting.ended | Встреча закончилась | meeting |
participant.joined | Кто-то зашёл | meeting, participant |
participant.left | Кто-то вышел | meeting, participant |
ping | Вы нажали «Отправить тестовое событие» | message |
События встреч приходят только по встречам, хозяин которых разрешил приложению meetings.read через «Войти через ReMeet».
Запрос
POST /remeet/webhook HTTP/1.1
Content-Type: application/json
User-Agent: ReMeet-Webhooks/1.0
ReMeet-Event: participant.joined
ReMeet-Delivery: 7d3f0c1e-1b0a-4a0f-9d55-2c3b9f4c6a10
ReMeet-Signature: t=1794067200,v1=5f2b…c9
{
"id": "7d3f0c1e-1b0a-4a0f-9d55-2c3b9f4c6a10",
"type": "participant.joined",
"created_at": "2026-11-07T12:00:00+00:00",
"app": "planning-poker",
"data": {
"meeting": { "id": "01m6saztdr6cafg9yzx83db0fs", "title": "Планёрка", "host_id": "p_3f9c…" },
"participant": { "id": "p_8a1d…", "name": "Омар" }
}
}Все id людей — id для вашего приложения (p_…), те же, что во встречах и в API.
Проверьте подпись
v1 — это HMAC-SHA256(secret, "<t>.<сырое тело>") в hex. Отклоняйте запросы с неверной подписью или с t старше 5 минут.
import crypto from 'node:crypto';
export function verifyReMeet(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(',').map((part) => part.split('=')));
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}import hashlib, hmac, time
def verify_remeet(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(part.split("=", 1) for part in header.split(","))
expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest()
return abs(time.time() - int(parts["t"])) < 300 and hmac.compare_digest(expected, parts["v1"])function verifyReMeet(string $rawBody, string $header, string $secret): bool
{
parse_str(str_replace(',', '&', $header), $parts);
$expected = hash_hmac('sha256', $parts['t'].'.'.$rawBody, $secret);
return abs(time() - (int) $parts['t']) < 300 && hash_equals($expected, $parts['v1']);
}Берите сырое тело, как оно пришло, до разбора JSON.
Ответ и повторы
Ответьте 2xx за 10 секунд — это значит «доставлено». Иначе ReMeet повторит: через 1 минуту, 5 минут и 30 минут (всего 4 попытки). Редиректы не выполняются. Одно событие может прийти дважды: убирайте дубли по ReMeet-Delivery.
Последние доставки и ответы вашего сервера видны в кабинете разработчика в разделе Последние доставки.
Правила
- Адрес — публичный
https(внутренние адреса и адреса частных сетей не принимаются). - Работу делайте после ответа: положите событие в очередь и сразу ответьте
200.