FastComments.com

Уебкуки

С FastComments е възможно да се извика API endpoint всеки път, когато коментар бъде добавен, актуализиран или премахнат от нашата система.

Постигаме това с помощта на асинхронни webhooks по HTTP/HTTPS.

Какво са уебкуките Internal Link

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

Поддържани събития и ресурси Internal Link

FastComments поддържа уебкуки само за ресурса Comment.

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

Всяко от тях се счита за отделно събитие в нашата система и като такова има различна семантика и структури за уебкук събитията.

Произволен брой крайни точки могат да се абонират за едно и също събитие, от таблото или чрез API (вижте Управление на уебкуки чрез API). Всяка уебкука се доставя независимо.

Тестване Internal Link

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

Проверка на полезния товар (Payload)

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

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

Уебхуковете, създадени преди въвеждането на схемата за подпис, също получават заглавка token, съдържаща вашия API Secret. Новите уебхукове не получават такава.

Използвайте проверката на HMAC подписа, за да сте сигурни, че полезните товари са автентични.

Инструменти за тестване

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

Типове събития

  • Create Event: Задейства се, когато е създаден нов коментар.
  • Update Event: Задейства се, когато коментар е редактиран.
  • Delete Event: Задейства се, когато коментар е изтрит.

Всеки уебхук е обвързан с едно събитие и един HTTP метод (POST, PUT или DELETE). Всяко събитие включва пълните данни за коментара в тялото на заявката (вижте Структури на данните за формата на полезния товар).

Структури от данни 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 е с префикс на идентификатора на наемателя. **/
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 /** Истина, ако потребителят е бил влязъл, когато е коментирал, или е верифицирал коментара, или е верифицирал сесията си, когато коментарът е оставен. **/
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 /** Номерът на страницата, на която се намира коментарът за сортиране "Most Relevant". **/
44 pageNumber: number | null
45 /** Номерът на страницата, на която се намира коментарът за сортиране "Oldest First". **/
46 pageNumberOF: number | null
47 /** Номерът на страницата, на която се намира коментарът за сортиране "Newest First". **/
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 Mentions
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** Идентификаторът на потребителя. За SSO потребители това ще има вашия tenant id като префикс. **/
4 id: string
5 /** Крайната @упомената текстова стойност, включително символа @. **/
6 tag: string
7 /** Оригиналният @упоменат текст, включително символа @. **/
8 rawTag: string
9 /** Какъв тип потребител е отбелязан. user = акаунт в FastComments.com. sso = SSOUser. **/
10 type: 'user'|'sso'
11 /** Ако потребителят се откаже от известия, това все пак ще бъде зададено на true. **/
12 sent: boolean
13}
14

Всеки елемент от списъка badges има следната структура. Вижте Badges guide за типове значки.

Обектът Webhook Badge
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 не трябва да създава дублирани обекти от ваша страна.

Заглавки на заявката

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

Вижте Security & API Tokens за информация относно проверка на HMAC подписа.

Сигурност и API токени Internal Link

FastComments webhook заявките включват множество механизми за автентикация с цел сигурност.

Изпращани хедъри

ХедърОписание
tokenВашият API Secret (за обратна съвместимост)
X-FastComments-TimestampUnix времеви печат (в секунди), когато заявката е подписана
X-FastComments-SignatureHMAC-SHA256 подпис на payload данните

Проверка на HMAC подписа (препоръчително)

Силно препоръчваме да проверявате HMAC подписа, за да сте сигурни, че webhook payload данните са автентични и не са били подменени.

Формат на подписа: 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;  // Предпазване от replay атаки
    }

    // Проверка на подписа
    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 проверка за подобрена сигурност, тъй като тя предпазва от replay атаки.

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

Уебхуковете могат да се управляват и чрез REST API. Така интеграции като Zapier се абонират за събития от коментари, без да докосват таблото, и това следва модела REST Hooks: абониране, получаване на събития, отписване.

API абонаментите съществуват редом с уебхуците, конфигурирани в таблото. Събитие от коментар се доставя на всеки уебхук, който съответства на неговия домейн, всяко като отделна доставка, независимо как е създаден уебхукът.

Автентикация

Всяка заявка се нуждае от вашия API Key в хедъра x-api-key (или параметъра API_KEY в заявката) и вашия tenant ID в параметъра 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ДаАбсолютен http или https URL.
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 за същото събитие и домейн връща съществуващия абонамент, вместо да създава дубликат, така че клиентът може безопасно да повтори заявката. Всеки tenant може да има до 50 API абонамента.

Списък

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

Връща всеки уебхук за tenant-а, включително тези, управлявани в таблото ("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 вместо него.

Примерни payloads

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 е лесна за разбиране и бърза за настройване.

Ако смятате, че сте открили пропуски в нашата документация, уведомете ни по-долу.