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

The only structure sent via webhooks is the WebhookComment object, outlined in TypeScript below.

The WebhookComment Object Structure

The "Create" Event Structure

The "create" event request body is a WebhookComment object.

The "Update" Event Structure

The "update" event request body is a WebhookComment object.

The "Delete" Event Structure

The "delete" event request body is a WebhookComment object.

Change as of Nov 14th 2023
Previously the "delete" event request body only contained the comment id. It now contains the full comment at the time of deletion.

Every key is always present in the body. When the comment has no value for a field the body carries null (or false for booleans and [] for lists), so the shape of a delivery never varies from one comment to the next.

Объект WebhookComment
Copy CopyRun External Link
1
2interface WebhookComment {
3 /** The id of the comment. **/
4 id: string
5 /** The id or URL that identifies the comment thread. Normalized. **/
6 urlId: string
7 /** The URL that points to where the comment was left. **/
8 url: string | null
9 /** The user id that left the comment. If SSO, prefixed with tenant id. **/
10 userId: string | null
11 /** The email of the user left the comment. **/
12 commenterEmail: string | null
13 /** The name of the user that shows in the comment widget. With SSO, can be displayName. **/
14 commenterName: string
15 /** Raw comment text. **/
16 comment: string
17 /** Comment text after parsing. **/
18 commentHTML: string
19 /** Comment external id. **/
20 externalId: string | null
21 /** The id of the parent comment. **/
22 parentId: string | null
23 /** The UTC date when the comment was left. **/
24 date: UTC_ISO_DateString
25 /** Combined karma (up - down) of votes. **/
26 votes: number
27 votesUp: number
28 votesDown: number
29 /** True if the user was logged in when they commented, or their verified the comment, or if they verified their session when the comment was left. **/
30 verified: boolean
31 /** The UTC date when the comment was verified. **/
32 verifiedDate: UTC_ISO_DateString | null
33 /** If a moderator marked the comment reviewed. **/
34 reviewed: boolean
35 /** The location, or base64 encoding, of the avatar. Will only be base64 if that was the value passed with SSO. **/
36 avatarSrc: string | null
37 /** Was the comment manually or automatically marked as spam? **/
38 isSpam: boolean
39 /** Was the comment automatically marked as spam? **/
40 aiDeterminedSpam: boolean
41 /** Are there images in the comment? **/
42 hasImages: boolean
43 /** The page number the comment is on for the "Most Relevant" sort direction. **/
44 pageNumber: number | null
45 /** The page number the comment is on for the "Oldest First" sort direction. **/
46 pageNumberOF: number | null
47 /** The page number the comment is on for the "Newest First" sort direction. **/
48 pageNumberNF: number | null
49 /** Was the comment approved automatically or manually? **/
50 approved: boolean
51 /** The locale code (format: en_us) of the user when the comment was written. **/
52 locale: string | null
53 /** The @mentions written in the comment that were successfully parsed. Empty when there are none. **/
54 mentions: CommentUserMention[]
55 /** The domain the comment is from. **/
56 domain: string | null
57 /** The moderation group ids associated with this comment. Empty when there are none. **/
58 moderationGroupIds: string[]
59}
60

When users are tagged in a comment, the information is stored in a list called mentions. Each object in that list has the following structure.

Объект упоминаний Webhook
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** The user id. For SSO users, this will have your tenant id prefixed. **/
4 id: string
5 /** The final @mention tag text, including the @ symbol. **/
6 tag: string
7 /** The original @mention tag text, including the @ symbol. **/
8 rawTag: string
9 /** What type of user was tagged. user = FastComments.com account. sso = SSOUser. **/
10 type: 'user'|'sso'
11 /** If the user opts out of notifications, this will still be set to true. **/
12 sent: boolean
13}
14

HTTP Methods

You can configure the HTTP method for each webhook event type in the admin panel:

  • Create Event: POST or PUT (default: PUT)
  • Update Event: POST or PUT (default: PUT)
  • Delete Event: DELETE, POST, or PUT (default: DELETE)

Since all requests contain an ID, Create and Update operations are idempotent by default (PUT). Repeating the same Create or Update request should not create duplicate objects on your side.

Request Headers

Each webhook request includes the following headers:

HeaderDescription
Content-Typeapplication/json
tokenYour API Secret
X-FastComments-TimestampUnix timestamp (seconds) when the request was signed
X-FastComments-SignatureHMAC-SHA256 signature (sha256=<hex>)

See Безопасность и токены API for information on verifying the HMAC signature.

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

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

Headers Sent

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

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

Signature Format: sha256=<hex-encoded-signature>

How the signature is computed:

  1. Concatenate: timestamp + "." + JSON_payload_body
  2. Compute HMAC-SHA256 using your API Secret as the key
  3. Hex-encode the result

Example Verification (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;  // Защита от повторного воспроизведения (replay-атаки)
    }

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

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

Example Verification (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}"

Example Verification (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);
}

Legacy Authentication

The token header containing your API Secret is still sent for backwards compatibility. However, we recommend migrating to HMAC verification for improved security as it protects against replay attacks.


Управление вебхуками через 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 окажется понятной и быстрой в настройке.

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