FastComments.com

Webhook

Con FastComments è possibile invocare un endpoint API ogni volta che un commento viene aggiunto, aggiornato o rimosso dal nostro sistema.

Realizziamo questo tramite webhook asincroni su HTTP/HTTPS.

Cosa sono i Webhook Internal Link

Un Webhook è un meccanismo, o un'integrazione, tra due sistemi in cui il "produttore" (FastComments) scatena un evento che il "consumatore" (tu) consuma tramite una chiamata API.

Eventi e risorse supportati Internal Link

FastComments supporta i webhook solo per la risorsa Comment.

Supportiamo i webhook per la creazione, la rimozione e l'aggiornamento dei commenti.

Ciascuno di questi è considerato un evento separato nel nostro sistema e, come tale, ha semantiche e strutture diverse per gli eventi webhook.

Un numero qualsiasi di endpoint può iscriversi allo stesso evento, dal dashboard o tramite l'API (vedi Managing Webhooks via the API). Ogni webhook viene consegnato in modo indipendente.

Test Internal Link

Le nuove pagine di webhook e quelle di modifica hanno un pulsante Send Test Payload che invia una richiesta all'URL attualmente presente nel modulo, sia che sia stato salvato o meno. Gli eventi Create e Update inviano un oggetto WebhookComment fittizio, mentre il test Delete invierà un corpo di richiesta fittizio contenente solo un ID.

Verifica dei payload

Durante il test della tua integrazione webhook, verifica che le richieste in arrivo includano le seguenti intestazioni:

  1. X-FastComments-Timestamp - timestamp Unix (secondi)
  2. X-FastComments-Signature - firma HMAC-SHA256

I webhook creati prima dell'introduzione dello schema di firma ricevono anche un'intestazione token contenente il tuo segreto API. I nuovi webhook no.

Utilizza la verifica della firma HMAC per garantire che i payload siano autentici.

Strumenti di test

Puoi utilizzare strumenti come webhook.site o ngrok per ispezionare i payload webhook in arrivo durante lo sviluppo.

Tipi di evento

  • Create Event: Attivato quando viene creato un nuovo commento.
  • Update Event: Attivato quando un commento viene modificato.
  • Delete Event: Attivato quando un commento viene eliminato.

Ogni webhook è associato a un singolo evento e a un metodo HTTP (POST, PUT o DELETE). Ogni evento include i dati completi del commento nel corpo della richiesta (vedi Data Structures per il formato del payload).

Strutture dati Internal Link

L'unica struttura inviata tramite webhook è l'oggetto WebhookComment, descritto di seguito in TypeScript.

La struttura dell'oggetto WebhookComment

La struttura dell'evento "Create"

Il corpo della richiesta dell'evento "create" è un oggetto WebhookComment.

La struttura dell'evento "Update"

Il corpo della richiesta dell'evento "update" è un oggetto WebhookComment.

La struttura dell'evento "Delete"

Il corpo della richiesta dell'evento "delete" è un oggetto WebhookComment.

Cambiamento del 14 novembre 2023
In precedenza il corpo della richiesta dell'evento "delete" conteneva solo l'id del commento. Ora contiene il commento completo al momento dell'eliminazione.

Ogni chiave è sempre presente nel corpo. Quando il commento non ha un valore per un campo, il corpo contiene null (o false per i booleani e [] per le liste), quindi la forma di una consegna non varia mai da un commento all'altro.

L'oggetto 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 /** The badges shown next to the commenter's name on this comment, as of when the event was created. Empty when there are none. **/
60 badges: CommentUserBadgeInfo[]
61}
62

Quando gli utenti vengono menzionati in un commento, le informazioni vengono memorizzate in una lista chiamata mentions. Ogni oggetto in quella lista ha la seguente struttura.

L'oggetto Webhook Mentions
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

Ogni voce nella lista badges ha la seguente struttura. Consulta la guida ai Badge per i tipi di badge.

L'oggetto Webhook Badge
Copy CopyRun External Link
1
2interface CommentUserBadgeInfo {
3 /** The id of the badge awarded to this user. **/
4 id: string
5 /** The badge type. **/
6 type: number
7 /** The badge description, shown when hovering over the badge. **/
8 description: string
9 /** The text shown on the badge. **/
10 displayLabel?: string | null
11 /** The image shown for the badge, if it uses one. **/
12 displaySrc?: string | null
13 backgroundColor?: string | null
14 borderColor?: string | null
15 textColor?: string | null
16 /** An extra CSS class applied to the badge. **/
17 cssClass?: string | null
18}
19

Metodi HTTP

Puoi configurare il metodo HTTP per ciascun tipo di evento webhook nel pannello di amministrazione:

  • Evento Create: POST o PUT (predefinito: PUT)
  • Evento Update: POST o PUT (predefinito: PUT)
  • Evento Delete: DELETE, POST o PUT (predefinito: DELETE)

Poiché tutte le richieste contengono un ID, le operazioni Create e Update sono idempotenti per impostazione predefinita (PUT). Ripetere la stessa richiesta Create o Update non dovrebbe creare oggetti duplicati da parte tua.

Header della richiesta

Ogni richiesta webhook include i seguenti header:

HeaderDescrizione
Content-Typeapplication/json
tokenIl tuo API Secret
X-FastComments-TimestampTimestamp Unix (secondi) in cui la richiesta è stata firmata
X-FastComments-SignatureFirma HMAC-SHA256 (sha256=<hex>)

Consulta Sicurezza e token API per informazioni sulla verifica della firma HMAC.

Sicurezza e token API Internal Link

FastComments webhook requests include multiple authentication mechanisms for security.

Intestazioni inviate

IntestazioneDescrizione
tokenIl tuo API Secret (per compatibilità con le versioni precedenti)
X-FastComments-TimestampTimestamp Unix (secondi) quando la richiesta è stata firmata
X-FastComments-SignatureFirma HMAC-SHA256 del payload

Verifica della firma HMAC (Consigliata)

Raccomandiamo vivamente di verificare la firma HMAC per assicurarsi che i payload dei webhook siano autentici e non siano stati manomessi.

Formato della firma: sha256=<hex-encoded-signature>

Come viene calcolata la firma:

  1. Concatena: timestamp + "." + JSON_payload_body
  2. Calcola HMAC-SHA256 usando il tuo API Secret come chiave
  3. Codifica il risultato in esadecimale

Esempio di verifica (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;
    }

    // Verifica che il timestamp sia recente (entro 5 minuti)
    const now = Math.floor(Date.now() / 1000);
    if (Math.abs(now - parseInt(timestamp, 10)) > 300) {
        return false;  // Prevenzione di attacchi di replay
    }

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

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

Esempio di verifica (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

    # Verifica che il timestamp sia recente
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
        return False

    # Verifica la firma
    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}"

Esempio di verifica (PHP)

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

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

    // Verifica che il timestamp sia recente (entro 5 minuti)
    $now = time();
    if (abs($now - intval($timestamp)) > 300) {
        return false;
    }

    // Verifica la firma
    $payload = json_encode($body, JSON_UNESCAPED_SLASHES);
    $message = $timestamp . '.' . $payload;
    $expectedSignature = 'sha256=' . hash_hmac('sha256', $message, $apiSecret);

    return hash_equals($expectedSignature, $signature);
}

Autenticazione legacy

L'intestazione token contenente il tuo API Secret viene ancora inviata per compatibilità con le versioni precedenti. Tuttavia, consigliamo di migrare alla verifica HMAC per una sicurezza migliorata poiché protegge dagli attacchi di replay.

Gestione dei webhook tramite API Internal Link

I webhook possono essere gestiti anche tramite la REST API. È così che integrazioni come Zapier si iscrivono agli eventi dei commenti senza toccare la dashboard, e segue il pattern REST Hooks: iscrizione, ricezione degli eventi, annullamento dell'iscrizione.

Le sottoscrizioni API convivono con i webhook configurati nella dashboard. Un evento di commento viene consegnato a ogni webhook che corrisponde al suo dominio, ciascuno come consegna separata, indipendentemente da come il webhook è stato creato.

Autenticazione

Ogni richiesta richiede la tua API Key nell'header x-api-key (o nel parametro query API_KEY) e il tuo tenant ID nel parametro query tenantId. Entrambi sono mostrati nella pagina API Secret della dashboard.

Iscrizione

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"
}
CampoObbligatorioDescrizione
urlSìUn URL http o https assoluto.
eventSìcomment-created, comment-updated o comment-deleted.
domainNoUn dominio dalla configurazione del tuo account. Il valore predefinito è *, che riceve gli eventi per ogni dominio.
methodNoPOST (predefinito), PUT o DELETE.

La risposta contiene la sottoscrizione:

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

Iscrivere nuovamente lo stesso URL allo stesso evento e dominio restituisce la sottoscrizione esistente invece di crearne un duplicato, quindi un client può riprovare in sicurezza. Ogni tenant può avere fino a 50 sottoscrizioni API.

Elenco

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

Restituisce ogni webhook per il tenant, inclusi quelli gestiti nella dashboard ("source": "dashboard"). Filtra con event, domain o source.

Annullamento iscrizione

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

Eliminare una sottoscrizione scarta anche gli eventi ancora in coda per essa. Solo le sottoscrizioni create tramite l'API possono essere eliminate in questo modo; un webhook della dashboard, o un id che non esiste sul tuo account, risponde 404 con codice not-found. I webhook della dashboard si modificano nella pagina Webhooks.

Payload e firma

Le consegne usano lo stesso payload dei webhook della dashboard (vedi Data Structures) e sono firmate con lo stesso schema HMAC (vedi Security & API Tokens). Le sottoscrizioni API non ricevono mai il header legacy token, quindi verifica invece l'header X-FastComments-Signature.

Payload di esempio

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

Restituisce i commenti più recenti dell'account esattamente nella forma in cui li porta una consegna, così un'integrazione può mostrare dati di esempio reali prima che arrivi il primo evento. event è opzionale e viene solo validato, poiché ogni evento consegna lo stesso oggetto commento. limit è predefinito a 3 e accetta da 1 a 10. Costa 2 crediti 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
        }
    ]
}

Rispondere con 410 Gone

Se l'endpoint di una sottoscrizione API risponde con HTTP 410 Gone, FastComments lo tratta come un annullamento dell'iscrizione: la sottoscrizione viene eliminata insieme ai suoi eventi in coda e non vengono tentate ulteriori consegne. I webhook configurati nella dashboard non vengono mai eliminati automaticamente; per loro un 410 è un normale fallimento. Qualsiasi altro stato di fallimento viene ritentato e alla fine disabilita il webhook, come descritto in How it Works & Handling Retries.

Dashboard

Le sottoscrizioni API appaiono nell'elenco Webhooks con la sorgente API, dove un amministratore può modificarle, disabilitarle, riattivarle o eliminarle.

In conclusione

Questa conclude la nostra documentazione sui Webhooks.

Speriamo che troviate l'integrazione FastComments Webhook facile da comprendere e veloce da configurare.

Se ritenete di aver individuato delle lacune nella nostra documentazione, fatecelo sapere qui sotto.