FastComments.com

Webhooks


Met FastComments is het mogelijk een API-eindpunt aan te roepen telkens wanneer een reactie aan ons systeem wordt toegevoegd, bijgewerkt of verwijderd.

Wij realiseren dit met asynchrone webhooks via HTTP/HTTPS.


Wat zijn Webhooks Internal Link

Een Webhook is een mechanisme, of een integratie, tussen twee systemen waar de "producent" (FastComments) een gebeurtenis die de "consument" (U) via een API-aanroep verwerkt.

Ondersteunde gebeurtenissen en bronnen Internal Link

FastComments ondersteunt webhooks alleen voor de Comment resource.

We ondersteunen webhooks voor het aanmaken, verwijderen en bijwerken van commentaar.

Elk van deze wordt beschouwd als een afzonderlijk evenement in ons systeem en heeft daarom verschillende semantiek en structuren voor de webhook‑evenementen.

Een willekeurig aantal eindpunten kan zich abonneren op hetzelfde evenement, via het dashboard of via de API (zie Managing Webhooks via de API). Elke webhook wordt onafhankelijk afgeleverd.

Testen Internal Link

De nieuwe en bewerk webhook‑pagina's hebben een Send Test Payload‑knop die een verzoek naar de URL in het formulier stuurt, ongeacht of deze is opgeslagen. De Create‑ en Update‑gebeurtenissen sturen een dummy WebhookComment‑object, terwijl bij het testen van Delete een dummy request‑body met alleen een ID wordt verzonden.

Verifiëren van payloads

Bij het testen van je webhook‑integratie, controleer of de binnenkomende verzoeken de volgende headers bevatten:

  1. X-FastComments-Timestamp – Unix‑tijdstempel (seconden)
  2. X-FastComments-Signature – HMAC‑SHA256‑handtekening

Webhooks die zijn aangemaakt vóór de introductie van het handtekeningschema ontvangen ook een token‑header met je API‑secret. Nieuwe webhooks niet.

Gebruik de HMAC‑handtekeningverificatie om te garanderen dat payloads authentiek zijn.

Testhulpmiddelen

Je kunt tools zoals webhook.site of ngrok gebruiken om binnenkomende webhook‑payloads tijdens de ontwikkeling te inspecteren.

Evenementtypen

  • Create Event: Wordt geactiveerd wanneer een nieuw commentaar wordt aangemaakt.
  • Update Event: Wordt geactiveerd wanneer een commentaar wordt bewerkt.
  • Delete Event: Wordt geactiveerd wanneer een commentaar wordt verwijderd.

Elke webhook is gekoppeld aan één evenement en één HTTP‑methode (POST, PUT of DELETE). Elk evenement bevat de volledige commentaargegevens in de request‑body (zie Data Structures voor het payload‑formaat).

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

Het WebhookComment-object
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.

Het Webhook Mentions-object
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 Security & API Tokens for information on verifying the HMAC signature.

Beveiliging & API-tokens Internal Link

FastComments webhookverzoeken bevatten meerdere authenticatiemechanismen voor beveiliging.

Verzonden headers

HeaderBeschrijving
tokenUw API Secret (voor achterwaartse compatibiliteit)
X-FastComments-TimestampUnix-timestamp (seconden) wanneer het verzoek werd ondertekend
X-FastComments-SignatureHMAC-SHA256-handtekening van de payload

HMAC-handtekeningverificatie (aanbevolen)

We raden sterk aan de HMAC-handtekening te verifiëren om ervoor te zorgen dat webhook-payloads authentiek zijn en niet zijn gewijzigd.

Handtekeningformaat: sha256=<hex-encoded-signature>

Hoe de handtekening wordt berekend:

  1. Samenvoegen: timestamp + "." + JSON_payload_body
  2. Bereken HMAC-SHA256 met uw API Secret als sleutel
  3. Hex-encodeer het resultaat

Voorbeeldverificatie (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;
    }

    // Controleer of de timestamp recent is (binnen 5 minuten)
    const now = Math.floor(Date.now() / 1000);
    if (Math.abs(now - parseInt(timestamp, 10)) > 300) {
        return false;  // Ter voorkoming van replay-aanvallen
    }

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

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

Voorbeeldverificatie (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

    # Controleer of de timestamp recent is
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
        return False

    # Verifieer handtekening
    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}"

Voorbeeldverificatie (PHP)

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

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

    // Controleer of de timestamp recent is (binnen 5 minuten)
    $now = time();
    if (abs($now - intval($timestamp)) > 300) {
        return false;
    }

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

    return hash_equals($expectedSignature, $signature);
}

Verouderde authenticatie

De token-header die uw API Secret bevat, wordt nog steeds verzonden voor achterwaartse compatibiliteit. We raden echter aan over te stappen op HMAC-verificatie voor verbeterde beveiliging, omdat dit beschermt tegen replay-aanvallen.

Webhooks beheren via de API Internal Link

Webhooks kunnen ook worden beheerd via de REST‑API. Dit is hoe integraties zoals Zapier zich abonneren op commentaar‑gebeurtenissen zonder het dashboard aan te raken, en het volgt het REST‑Hooks‑patroon: abonneren, gebeurtenissen ontvangen, afmelden.

API‑abonnementen bestaan naast de webhooks die in het dashboard zijn geconfigureerd. Een commentaar‑gebeurtenis wordt geleverd aan elke webhook die overeenkomt met zijn domein, elk als een eigen levering, ongeacht hoe de webhook is aangemaakt.

Authenticatie

Elke aanvraag heeft uw API‑sleutel nodig in de x-api-key‑header (of de API_KEY‑queryparameter) en uw tenant‑ID in de tenantId‑queryparameter. Beide worden weergegeven op de API‑Secret‑pagina in het dashboard.

Abonneren

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"
}
VeldVerplichtBeschrijving
urlJaEen absolute http‑ of https‑URL.
eventJacomment-created, comment-updated of comment-deleted.
domainNeeEen domein uit uw accountconfiguratie. Standaard *, wat gebeurtenissen voor elk domein ontvangt.
methodNeePOST (standaard), PUT of DELETE.

De respons bevat het abonnement:

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

Het opnieuw abonneren van dezelfde URL op dezelfde gebeurtenis en hetzelfde domein retourneert het bestaande abonnement in plaats van een duplicaat te maken, zodat een client veilig kan herhalen. Elke tenant kan tot 50 API‑abonnementen hebben.

Lijst

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

Retourneert elke webhook voor de tenant, inclusief die beheerd in het dashboard ("source": "dashboard"). Filter met event, domain of source.

Afmelden

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

Het verwijderen van een abonnement verwijdert ook eventuele nog in de wachtrij staande gebeurtenissen. Alleen abonnementen die via de API zijn aangemaakt, kunnen op deze manier worden verwijderd; een dashboard‑webhook, of een id die niet bestaat in uw account, geeft 404 met code not-found. Dashboard‑webhooks worden bewerkt op de Webhooks‑pagina.

Payloads en ondertekening

Leveringen gebruiken dezelfde payload als dashboard‑webhooks (zie Data Structures) en worden ondertekend met hetzelfde HMAC‑schema (zie Security & API Tokens). API‑abonnementen ontvangen nooit de legacy token‑header, dus verifieer in plaats daarvan de X-FastComments-Signature‑header.

Voorbeeldpayloads

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

Retourneert de meest recente commentaren van het account in precies de vorm die een levering heeft, zodat een integratie echte voorbeeldgegevens kan tonen voordat de eerste gebeurtenis arriveert. event is optioneel en alleen gevalideerd, aangezien elke gebeurtenis hetzelfde commentaarobject levert. limit standaard op 3 en accepteert 1 tot 10. Kost 2 API‑credits.

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

Reageren met 410 Gone

Als het eindpunt van een API‑abonnement reageert met HTTP 410 Gone, behandelt FastComments dat als een afmelding: het abonnement wordt verwijderd samen met de in de wachtrij staande gebeurtenissen, en er worden geen verdere leveringen meer geprobeerd. Webhooks die in het dashboard zijn geconfigureerd, worden nooit automatisch verwijderd; voor hen is een 410 een gewone fout. Elke andere foutstatus wordt opnieuw geprobeerd en schakelt uiteindelijk de webhook uit, zoals beschreven in How it Works & Handling Retries.

Dashboard

API‑abonnementen verschijnen in de Webhooks‑lijst met de bron API, waar een beheerder ze kan bewerken, uitschakelen, opnieuw inschakelen of verwijderen.


Tot slot

Hiermee is onze Webhooks-documentatie afgerond.

We hopen dat je de FastComments Webhook-integratie gemakkelijk te begrijpen vindt en snel kunt instellen.

Als je denkt dat er hiaten in onze documentatie zitten, laat het ons hieronder weten.