FastComments.com

Вебхуки

С FastComments можно вызывать конечную точку API всякий раз, когда комментарий добавляется, обновляется или удаляется в нашей системе.

Мы реализуем это с помощью асинхронных вебхуков по протоколам HTTP/HTTPS.

Что такое вебхуки Internal Link

Вебхук — это механизм, или интеграция, между двумя системами, где "производитель" (FastComments) генерирует событие которое "потребитель" (Вы) получает посредством вызова API.

Поддерживаемые события и ресурсы Internal Link

FastComments поддерживает вебхуки только для ресурса Comment.

Мы поддерживаем вебхуки для создания комментариев, их удаления и обновления.

Каждое из этих действий считается отдельным событием в нашей системе, поэтому у вебхуков разных событий разные семантика и структура.

Любое количество конечных точек может подписаться на одно и то же событие, через панель управления или через API (см. Управление вебхуками через API). Каждый вебхук доставляется независимо.

Тестирование Internal Link

Новые и страницы редактирования вебхуков имеют кнопку Send Test Payload, которая отправляет запрос на URL, указанный в форме, независимо от того, был он сохранён или нет. События Create и Update отправляют фиктивный объект WebhookComment, а при тестировании Delete будет отправлено фиктивное тело запроса, содержащее только ID.

Проверка полезных данных

При тестировании интеграции вебхука убедитесь, что входящие запросы содержат следующие заголовки:

  1. X-FastComments-Timestamp – Unix‑временная метка (секунды)
  2. X-FastComments-Signature – подпись HMAC‑SHA256

Вебхуки, созданные до внедрения схемы подписи, также получают заголовок token, содержащий ваш API‑секрет. Новые вебхуки его не получают.

Используйте проверку подписи HMAC, чтобы убедиться в подлинности полезных данных.

Инструменты тестирования

Вы можете использовать такие инструменты, как webhook.site или ngrok, чтобы просматривать входящие полезные данные вебхуков во время разработки.

Типы событий

  • Create Event: Событие, вызываемое при создании нового комментария.
  • Update Event: Событие, вызываемое при редактировании комментария.
  • Delete Event: Событие, вызываемое при удалении комментария.

Каждый вебхук привязан к одному событию и одному HTTP‑методу (POST, PUT или DELETE). Каждое событие включает полные данные комментария в теле запроса (см. Data Structures для формата полезных данных).

Структуры данных Internal Link

Единственная структура, отправляемая через вебхуки, — объект WebhookComment, описанный ниже на TypeScript.

Структура объекта WebhookComment

Структура события "Create"

Тело запроса события "create" представляет собой объект WebhookComment.

Структура события "Update"

Тело запроса события "update" представляет собой объект WebhookComment.

Структура события "Delete"

Тело запроса события "delete" представляет собой объект WebhookComment.

Изменения от 14 ноября 2023 г.
Ранее тело запроса события "delete" содержало только идентификатор комментария. Теперь оно содержит полный комментарий на момент удаления.

Каждый ключ всегда присутствует в теле. Когда у комментария нет значения для поля, тело содержит null
(или false для булевых значений и [] для списков), поэтому структура доставки никогда не меняется от комментария к комментарию.

Объект WebhookComment
Copy CopyRun External Link
1
2interface WebhookComment {
3 /** Идентификатор комментария. **/
4 id: string
5 /** Идентификатор или URL, определяющий ветку комментариев. Нормализовано. **/
6 urlId: string
7 /** URL, указывающий место, где был оставлен комментарий. **/
8 url: string | null
9 /** Идентификатор пользователя, оставившего комментарий. Если SSO, с префиксом tenant id. **/
10 userId: string | null
11 /** Электронная почта пользователя, оставившего комментарий. **/
12 commenterEmail: string | null
13 /** Имя пользователя, отображаемое в виджете комментария. При SSO может быть displayName. **/
14 commenterName: string
15 /** Необработанный текст комментария. **/
16 comment: string
17 /** Текст комментария после парсинга. **/
18 commentHTML: string
19 /** Внешний идентификатор комментария. **/
20 externalId: string | null
21 /** Идентификатор родительского комментария. **/
22 parentId: string | null
23 /** Дата в UTC, когда был оставлен комментарий. **/
24 date: UTC_ISO_DateString
25 /** Суммарный карма (голосов "за" - "против"). **/
26 votes: number
27 votesUp: number
28 votesDown: number
29 /** true, если пользователь был залогинен при комментировании, или их комментарий был верифицирован, или они верифицировали сессию при оставлении комментария. **/
30 verified: boolean
31 /** Дата в UTC, когда комментарий был верифицирован. **/
32 verifiedDate: UTC_ISO_DateString | null
33 /** Если модератор отметил комментарий как проверенный. **/
34 reviewed: boolean
35 /** Расположение или base64-кодировка аватара. Будет base64 только если это значение было передано через SSO. **/
36 avatarSrc: string | null
37 /** Был ли комментарий помечен как спам вручную или автоматически? **/
38 isSpam: boolean
39 /** Был ли комментарий автоматически помечен как спам? **/
40 aiDeterminedSpam: boolean
41 /** Есть ли изображения в комментарии? **/
42 hasImages: boolean
43 /** Номер страницы, на которой находится комментарий при сортировке "Самые релевантные". **/
44 pageNumber: number | null
45 /** Номер страницы, на которой находится комментарий при сортировке "Старые сначала". **/
46 pageNumberOF: number | null
47 /** Номер страницы, на которой находится комментарий при сортировке "Новые сначала". **/
48 pageNumberNF: number | null
49 /** Был ли комментарий одобрен автоматически или вручную? **/
50 approved: boolean
51 /** Код локали (формат: en_us) пользователя, когда был написан комментарий. **/
52 locale: string | null
53 /** @упоминания, написанные в комментарии и успешно распарсенные. Пусто, если их нет. **/
54 mentions: CommentUserMention[]
55 /** Домен, откуда пришел комментарий. **/
56 domain: string | null
57 /** Идентификаторы групп модерации, связанные с этим комментарием. Пусто, если их нет. **/
58 moderationGroupIds: string[]
59 /** Значки, отображаемые рядом с именем комментатора в этом комментарии, на момент создания события. Пусто, если их нет. **/
60 badges: CommentUserBadgeInfo[]
61}
62

Когда пользователи упоминаются в комментарии, информация сохраняется в списке под названием mentions. Каждый объект в этом списке имеет следующую структуру.

Объект упоминаний Webhook
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** Идентификатор пользователя. Для SSO‑пользователей будет с префиксом tenant id. **/
4 id: string
5 /** Окончательный текст тега @mention, включая символ @. **/
6 tag: string
7 /** Оригинальный текст тега @mention, включая символ @. **/
8 rawTag: string
9 /** Какой тип пользователя был упомянут. user = аккаунт FastComments.com. sso = SSOUser. **/
10 type: 'user'|'sso'
11 /** Если пользователь отказался от уведомлений, это всё равно будет true. **/
12 sent: boolean
13}
14

Каждый элемент списка badges имеет следующую структуру. Смотрите руководство по значкам для типов значков.

Объект значка Webhook
Copy CopyRun External Link
1
2interface CommentUserBadgeInfo {
3 /** Идентификатор значка, присвоенного этому пользователю. **/
4 id: string
5 /** Тип значка. **/
6 type: number
7 /** Описание значка, отображаемое при наведении. **/
8 description: string
9 /** Текст, отображаемый на значке. **/
10 displayLabel?: string | null
11 /** Изображение, отображаемое для значка, если оно используется. **/
12 displaySrc?: string | null
13 backgroundColor?: string | null
14 borderColor?: string | null
15 textColor?: string | null
16 /** Дополнительный CSS‑класс, применяемый к значку. **/
17 cssClass?: string | null
18}
19

HTTP-методы

Вы можете настроить HTTP-метод для каждого типа события вебхука в админ-панели:

  • Create Event: POST или PUT (по умолчанию: PUT)
  • Update Event: POST или PUT (по умолчанию: PUT)
  • Delete Event: DELETE, POST или PUT (по умолчанию: DELETE)

Поскольку все запросы содержат ID, операции Create и Update по умолчанию идемпотентны (PUT). Повторение того же запроса Create или Update не должно создавать дублирующие объекты у вас.

Заголовки запроса

Каждый запрос вебхука включает следующие заголовки:

HeaderОписание
Content-Typeapplication/json
tokenВаш API‑секрет
X-FastComments-TimestampUnix‑временная метка (секунды) при подписи запроса
X-FastComments-SignatureHMAC‑SHA256 подпись (sha256=<hex>)

Смотрите Безопасность и API‑токены для информации о проверке HMAC‑подписи.

Безопасность и токены API Internal Link

Запросы вебхуков FastComments включают несколько механизмов аутентификации для обеспечения безопасности.

Отправляемые заголовки

ЗаголовокОписание
tokenВаш API Secret (для обратной совместимости)
X-FastComments-TimestampUnix-время (в секундах), когда запрос был подписан
X-FastComments-SignatureПодпись HMAC-SHA256 полезной нагрузки

Проверка подписи HMAC (рекомендуется)

Мы настоятельно рекомендуем проверять подпись HMAC, чтобы убедиться, что полезная нагрузка вебхука подлинная и не была изменена.

Формат подписи: sha256=<hex-encoded-signature>

Как вычисляется подпись:

  1. Конкатенация: timestamp + "." + JSON_payload_body
  2. Вычисление HMAC-SHA256 с использованием вашего API Secret в качестве ключа
  3. Кодирование результата в hex

Пример проверки (Node.js)

const crypto = require('crypto');

function verifyWebhookSignature(req, apiSecret) {
    const timestamp = req.headers['x-fastcomments-timestamp'];
    const signature = req.headers['x-fastcomments-signature'];

    if (!timestamp || !signature) {
        return false;
    }

    // Проверка, что временная метка свежая (в пределах 5 минут)
    const now = Math.floor(Date.now() / 1000);
    if (Math.abs(now - parseInt(timestamp, 10)) > 300) {
        return false;  // Защита от атак повторного воспроизведения
    }

    // Проверка подписи
    const payload = JSON.stringify(req.body);
    const expectedSignature = crypto
        .createHmac('sha256', apiSecret)
        .update(`${timestamp}.${payload}`)
        .digest('hex');

    return signature === `sha256=${expectedSignature}`;
}

Пример проверки (Python)

import hmac
import hashlib
import time
import json

def verify_webhook_signature(headers, body, api_secret):
    timestamp = headers.get('X-FastComments-Timestamp')
    signature = headers.get('X-FastComments-Signature')

    if not timestamp or not signature:
        return False

    # Проверка, что временная метка свежая
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
        return False

    # Проверка подписи
    payload = json.dumps(body, separators=(',', ':'))
    message = f"{timestamp}.{payload}"
    expected = hmac.new(
        api_secret.encode(),
        message.encode(),
        hashlib.sha256
    ).hexdigest()

    return signature == f"sha256={expected}"

Пример проверки (PHP)

function verifyWebhookSignature($headers, $body, $apiSecret) {
    $timestamp = $headers['X-FastComments-Timestamp'] ?? null;
    $signature = $headers['X-FastComments-Signature'] ?? null;

    if (!$timestamp || !$signature) {
        return false;
    }

    // Проверка, что временная метка свежая (в пределах 5 минут)
    $now = time();
    if (abs($now - intval($timestamp)) > 300) {
        return false;
    }

    // Проверка подписи
    $payload = json_encode($body, JSON_UNESCAPED_SLASHES);
    $message = $timestamp . '.' . $payload;
    $expectedSignature = 'sha256=' . hash_hmac('sha256', $message, $apiSecret);

    return hash_equals($expectedSignature, $signature);
}

Устаревшая аутентификация

Заголовок token, содержащий ваш API Secret, по-прежнему отправляется для обратной совместимости. Однако мы рекомендуем перейти на проверку HMAC для повышения безопасности, так как она защищает от атак повторного воспроизведения.

Управление вебхуками через API Internal Link

Webhooks также могут управляться через REST API. Так интеграции, такие как Zapier, подписываются на события комментариев, не открывая панель управления, и следуют шаблону REST Hooks: подписка, получение событий, отписка.

Подписки API находятся рядом с вебхуками, настроенными в панели управления. Событие комментария доставляется каждому вебхуку, который соответствует его домену, каждое как отдельная доставка, независимо от того, как вебхук был создан.

Аутентификация

Каждый запрос требует ваш API‑ключ в заголовке x-api-key (или параметр запроса API_KEY) и идентификатор арендатора в параметреестре tenantId. Оба отображаются на странице API Secret в панели управления.

Подписка

POST https://fastcomments.com/api/v1/webhooks?tenantId=YOUR_TENANT_ID
x-api-key: YOUR_API_KEY
Content-Type: application/json

{
    "url": "https://hooks.zapier.com/hooks/catch/123/abc",
    "event": "comment-created"
}
ПолеОбязательноОписание
urlДаАбсолютный URL http или https.
eventДаcomment-created, comment-updated или comment-deleted.
domainНетДомен из конфигурации вашей учётной записи. По умолчанию *, который получает события для всех доменов.
methodНетPOST (по умолчанию), PUT или DELETE.

Ответ содержит подписку:

{
    "status": "success",
    "webhook": {
        "id": "66f1c4c1e7a2b3d4f5a6b7c8",
        "url": "https://hooks.zapier.com/hooks/catch/123/abc",
        "event": "comment-created",
        "domain": "*",
        "method": "POST",
        "source": "api",
        "enabled": true,
        "createdAt": "2026-09-08T12:00:00.000Z"
    }
}

Подписка той же URL на то же событие и домен снова возвращает существующую подписку, а не создаёт дубликат, поэтому клиент может безопасно повторять запрос. Каждый арендатор может иметь до 50 подписок API.

Список

GET https://fastcomments.com/api/v1/webhooks?tenantId=YOUR_TENANT_ID

Возвращает каждый вебхук арендатора, включая управляемые в панели ("source": "dashboard"). Фильтруйте по event, domain или source.

Отписка

DELETE https://fastcomments.com/api/v1/webhooks/SUBSCRIPTION_ID?tenantId=YOUR_TENANT_ID

Удаление подписки также отбрасывает любые события, ещё находящиеся в очереди. Только подписки, созданные через API, могут быть удалены таким способом; вебхук из панели управления или идентификатор, который не существует в вашей учётной записи, возвращает 404 с кодом not-found. Вебхуки из панели управления редактируются на странице Webhooks.

Полезные нагрузки и подпись

Доставки используют тот же полезный payload, что и вебхуки из панели (см. Data Structures) и подписываются тем же схемой HMAC (см. Security & API Tokens). Подписки API никогда не получают устаревший заголовок token, поэтому проверяйте заголовок X-FastComments-Signature вместо него.

Пример полезных нагрузок

GET https://fastcomments.com/api/v1/webhooks/sample-payloads?tenantId=YOUR_TENANT_ID&event=comment-created&limit=3

Возвращает самые последние комментарии учётной записи точно в том виде, в котором их передаёт доставка, поэтому интеграция может показать реальные примерные данные до поступления первого события. event необязателен и только проверяется, поскольку каждое событие передаёт один и тот же объект комментария. limit по умолчанию 3 и принимает значения от 1 до 10. Стоимость — 2 кредита API.

{
    "status": "success",
    "payloads": [
        {
            "id": "66f1c4c1e7a2b3d4f5a6b7c8",
            "urlId": "https://example.com/blog/hello-world",
            "commenterName": "Jane Reader",
            ",
            "comment": "Great article!",
            "date": "2026-09-08T12:00:00.000Z",
            "approved": true
        }
    ]
}

Ответ с 410 Gone

Если конечная точка подписки API отвечает HTTP 410 Gone, FastComments рассматривает это как отписку: подписка удаляется вместе с её ожидающими событиями, и дальнейшие доставки не производятся. Вебхуки, настроенные в панели, никогда не удаляются автоматически; для них 410 — обычная ошибка. Любой другой статус ошибки повторяется и в конечном итоге отключает вебхук, как описано в разделе How it Works & Handling Retries.

Панель управления

Подписки API отображаются в списке Webhooks с источником API, где администратор может редактировать, отключать, повторно включать или удалять их.

В заключение

На этом завершается наша документация по Webhooks.

Мы надеемся, что интеграция FastComments Webhook окажется понятной и быстрой в настройке.

Если вы считаете, что обнаружили какие-либо пробелы в нашей документации, сообщите нам об этом ниже.