FastComments.com

Webhooki


Z FastComments możliwe jest wywołanie punktu końcowego API za każdym razem, gdy komentarz zostanie dodany, zaktualizowany lub usunięty z naszego systemu.

Realizujemy to za pomocą asynchronicznych webhooków przez HTTP/HTTPS.


Czym są webhooki Internal Link


Webhook to mechanizm, lub integracja, pomiędzy dwoma systemami gdzie "producent" (FastComments) wyzwala zdarzenie które "konsument" (Ty) odbiera za pomocą wywołania API.


Obsługiwane zdarzenia i zasoby Internal Link

FastComments obsługuje webhooki tylko dla zasobu Comment.

Obsługujemy webhooki dla tworzenia komentarzy, ich usuwania oraz aktualizacji.

Każdy z nich jest traktowany jako osobne zdarzenie w naszym systemie i w związku z tym ma różne semantyki i struktury zdarzeń webhooków.

Dowolna liczba endpointów może subskrybować to samo zdarzenie, z poziomu panelu sterowania lub poprzez API (zobacz Managing Webhooks via the API). Każdy webhook jest dostarczany niezależnie.

Testowanie Internal Link

Nowe i edytowane strony webhooków mają przycisk Send Test Payload, który wysyła żądanie do adresu URL aktualnie znajdującego się w formularzu, niezależnie od tego, czy został on zapisany. Zdarzenia Create i Update wysyłają przykładowy obiekt WebhookComment, natomiast testowanie Delete wyśle przykładowe ciało żądania zawierające jedynie identyfikator.

Weryfikacja ładunków

Podczas testowania integracji webhooka, sprawdź, czy przychodzące żądania zawierają następujące nagłówki:

  1. X-FastComments-Timestamp – znacznik czasu Unix (sekundy)
  2. X-FastComments-Signature – podpis HMAC‑SHA256

Webhooki utworzone przed wprowadzeniem schematu podpisu otrzymują również nagłówek token zawierający Twój sekret API. Nowe webhooki go nie mają.

Użyj weryfikacji podpisu HMAC, aby zapewnić autentyczność ładunków.

Narzędzia testowe

Możesz używać narzędzi takich jak webhook.site lub ngrok, aby przeglądać przychodzące ładunki webhooków podczas programowania.

Typy zdarzeń

  • Zdarzenie Create: wywoływane, gdy zostaje utworzony nowy komentarz.
  • Zdarzenie Update: wywoływane, gdy komentarz zostaje edytowany.
  • Zdarzenie Delete: wywoływane, gdy komentarz zostaje usunięty.

Każdy webhook jest powiązany z jednym zdarzeniem i jedną metodą HTTP (POST, PUT lub DELETE). Każde zdarzenie zawiera pełne dane komentarza w ciele żądania (zobacz Data Structures po format ładunku).

Struktury danych Internal Link

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

Struktura obiektu WebhookComment

Struktura zdarzenia „Create”

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

Struktura zdarzenia „Update”

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

Struktura zdarzenia „Delete”

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.

Obiekt 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.

Obiekt Wzmianki 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

Metody HTTP

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

  • Zdarzenie Create: POST or PUT (default: PUT)
  • Zdarzenie Update: POST or PUT (default: PUT)
  • Zdarzenie Delete: 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.

Nagłówki żądania

Each webhook request includes the following headers:

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

See Security & API Tokens for information on verifying the HMAC signature.

Bezpieczeństwo i tokeny API Internal Link

FastComments webhook requests include multiple authentication mechanisms for security.

Wysyłane nagłówki

NagłówekOpis
tokenTwój API Secret (w celu zachowania zgodności wstecznej)
X-FastComments-TimestampZnacznik czasu Unix (sekundy), kiedy żądanie zostało podpisane
X-FastComments-SignaturePodpis HMAC-SHA256 ładunku

Weryfikacja podpisu HMAC (zalecane)

Zdecydowanie zalecamy weryfikację podpisu HMAC, aby upewnić się, że ładunki webhooków są autentyczne i nie zostały zmienione.

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

Jak obliczany jest podpis:

  1. Połącz: timestamp + "." + JSON_payload_body
  2. Oblicz HMAC-SHA256 używając swojego API Secret jako klucza
  3. Zakoduj wynik w hex

Przykład weryfikacji (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;
    }

    // Zweryfikuj, że znacznik czasu jest aktualny (w ciągu 5 minut)
    const now = Math.floor(Date.now() / 1000);
    if (Math.abs(now - parseInt(timestamp, 10)) > 300) {
        return false;  // Zapobieganie atakom powtórzeniowym
    }

    // Verify signature
    const payload = JSON.stringify(req.body);
    const expectedSignature = crypto
        .createHmac('sha256', apiSecret)
        .update(`${timestamp}.${payload}`)
        .digest('hex');

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

Przykład weryfikacji (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

    # Zweryfikuj, że znacznik czasu jest aktualny
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
        return False

    # Zweryfikuj podpis
    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}"

Przykład weryfikacji (PHP)

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

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

    // Zweryfikuj, że znacznik czasu jest aktualny (w ciągu 5 minut)
    $now = time();
    if (abs($now - intval($timestamp)) > 300) {
        return false;
    }

    // Zweryfikuj podpis
    $payload = json_encode($body, JSON_UNESCAPED_SLASHES);
    $message = $timestamp . '.' . $payload;
    $expectedSignature = 'sha256=' . hash_hmac('sha256', $message, $apiSecret);

    return hash_equals($expectedSignature, $signature);
}

Starsze uwierzytelnianie

Nagłówek token zawierający Twój API Secret wciąż jest wysyłany dla zachowania zgodności wstecznej. Jednak zalecamy przejście na weryfikację HMAC dla lepszego bezpieczeństwa, ponieważ chroni ona przed atakami powtórzeniowymi.

Zarządzanie webhookami za pomocą API Internal Link

Webhooks można również zarządzać za pośrednictwem REST API. Tak integracje takie jak Zapier subskrybują zdarzenia komentarzy bez użycia panelu, i stosują wzorzec REST Hooks: subskrybuj, odbieraj zdarzenia, wypisz się.

Subskrypcje API współistnieją z webhookami skonfigurowanymi w panelu. Zdarzenie komentarza jest dostarczane do każdego webhooka, który pasuje do jego domeny, każde jako osobna dostawa, niezależnie od tego, w jaki sposób webhook został utworzony.

Uwierzytelnianie

Każde żądanie wymaga Twojego klucza API w nagłówku x-api-key (lub parametrze zapytania API_KEY) oraz identyfikatora najemcy w parametrze zapytania tenantId. Oba są wyświetlane na stronie API Secret w panelu.

Subskrypcja

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"
}
PoleWymaganeOpis
urlTakBezwzględny adres URL http lub https.
eventTakcomment-created, comment-updated lub comment-deleted.
domainNieDomena z konfiguracji Twojego konta. Domyślnie *, co oznacza odbieranie zdarzeń ze wszystkich domen.
methodNiePOST (domyślnie), PUT lub DELETE.

Odpowiedź zawiera subskrypcję:

{
    "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"
    }
}

Subskrybowanie tego samego URL do tego samego zdarzenia i domeny ponownie zwraca istniejącą subskrypcję zamiast tworzyć duplikat, więc klient może bezpiecznie ponowić próbę. Każdy najemca może mieć maksymalnie 50 subskrypcji API.

Lista

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

Zwraca wszystkie webhooki dla najemcy, w tym te zarządzane w panelu ("source": "dashboard"). Filtruj za pomocą event, domain lub source.

Anulowanie subskrypcji

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

Usunięcie subskrypcji usuwa również wszystkie zdarzenia, które nadal są w kolejce. Tylko subskrypcje utworzone przez API mogą być usunięte w ten sposób; webhook z panelu lub identyfikator nieistniejący na Twoim koncie zwraca 404 z kodem not-found. Webhooki z panelu są edytowane na stronie Webhooks.

Ładunki i podpisy

Dostawy używają takiego samego ładunku jak webhooki z panelu (zobacz Struktury Danych) i są podpisane tym samym schematem HMAC (zobacz Bezpieczeństwo & Tokeny API). Subskrypcje API nigdy nie otrzymują starszego nagłówka token, więc należy weryfikować nagłówek X-FastComments-Signature.

Przykładowe ładunki

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

Zwraca najnowsze komentarze konta w dokładnym formacie, jaki ma dostawa, dzięki czemu integracja może wyświetlić rzeczywiste przykładowe dane przed przyjściem pierwszego zdarzenia. event jest opcjonalny i jedynie walidowany, ponieważ każde zdarzenie dostarcza ten sam obiekt komentarza. limit domyślnie wynosi 3 i przyjmuje wartości od 1 do 10. Kosztuje 2 kredyty 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
        }
    ]
}

Odpowiadanie kodem 410 Gone

Jeśli endpoint subskrypcji API odpowie kodem HTTP 410 Gone, FastComments traktuje to jako wypisanie się: subskrypcja jest usuwana wraz z oczekującymi zdarzeniami i nie są podejmowane dalsze próby dostawy. Webhooki skonfigurowane w panelu nigdy nie są usuwane automatycznie; dla nich 410 oznacza zwykłą awarię. Każdy inny kod błędu jest ponawiany i ostatecznie wyłącza webhook, jak opisano w sekcji Jak to działa & Obsługa ponowień.

Panel

Subskrypcje API pojawiają się na liście Webhooks ze źródłem API, gdzie administrator może je edytować, wyłączać, ponownie włączać lub usuwać.


Podsumowanie

To kończy naszą dokumentację Webhooks.

Mamy nadzieję, że integracja FastComments Webhook jest łatwa do zrozumienia i szybka do skonfigurowania.

Jeśli uważasz, że zidentyfikowałeś jakiekolwiek luki w naszej dokumentacji, daj nam znać poniżej.