FastComments.com

Webhooks

Avec FastComments, il est possible d'appeler un point de terminaison d'API chaque fois qu'un commentaire est ajouté, mis à jour ou supprimé de notre système.

Nous y parvenons grâce à des webhooks asynchrones via HTTP/HTTPS.

Qu'est-ce que les webhooks Internal Link

Un Webhook est un mécanisme, ou une intégration, entre deux systèmes où le "producteur" (FastComments) déclenche un événement que le "consommateur" (Vous) consomme via un appel d'API.

Événements et ressources pris en charge Internal Link

FastComments prend en charge les webhooks uniquement pour la ressource Comment.

Nous prenons en charge les webhooks pour la création, la suppression et la mise à jour des commentaires.

Chacun de ces cas est considéré comme un événement distinct dans notre système et possède donc des sémantiques et des structures différentes pour les événements webhook.

Un nombre quelconque de points de terminaison peut s'abonner au même événement, depuis le tableau de bord ou via l'API (voir Gestion des webhooks via l'API). Chaque webhook est livré indépendamment.

Tests Internal Link

Les nouvelles pages de webhook et les pages d'édition disposent d'un bouton Send Test Payload qui envoie une requête à l'URL actuellement dans le formulaire, qu'elle ait été enregistrée ou non. Les événements Create et Update envoient un objet WebhookComment factice, tandis que le test de Delete enverra un corps de requête factice contenant uniquement un ID.

Vérification des charges utiles

Lors du test de votre intégration webhook, vérifiez que les requêtes entrantes incluent les en‑têtes suivants :

  1. X-FastComments-Timestamp – horodatage Unix (secondes)
  2. X-FastComments-Signature – signature HMAC‑SHA256

Les webhooks créés avant l’introduction du schéma de signature reçoivent également un en‑tête token contenant votre secret d’API. Les nouveaux webhooks ne le font pas.

Utilisez la vérification de signature HMAC pour garantir l’authenticité des charges utiles.

Outils de test

Vous pouvez utiliser des outils comme webhook.site ou ngrok pour inspecter les charges utiles webhook entrantes pendant le développement.

Types d'événements

  • Create Event : déclenché lorsqu’un nouveau commentaire est créé.
  • Update Event : déclenché lorsqu’un commentaire est modifié.
  • Delete Event : déclenché lorsqu’un commentaire est supprimé.

Chaque webhook est associé à un seul événement et à une méthode HTTP (POST, PUT ou DELETE). Chaque événement inclut les données complètes du commentaire dans le corps de la requête (voir Data Structures pour le format de la charge utile).

Structures de données Internal Link

La seule structure envoyée via les webhooks est l'objet WebhookComment, décrit en TypeScript ci‑dessous.

Structure de l'objet WebhookComment

Structure de l'événement "Create"

Le corps de la requête de l'événement "create" est un objet WebhookComment.

Structure de l'événement "Update"

Le corps de la requête de l'événement "update" est un objet WebhookComment.

Structure de l'événement "Delete"

Le corps de la requête de l'événement "delete" est un objet WebhookComment.

Modification à partir du 14 novembre 2023
Auparavant, le corps de la requête de l'événement "delete" ne contenait que l'identifiant du commentaire. Il contient maintenant le commentaire complet au moment de la suppression.

Chaque clé est toujours présente dans le corps. Lorsque le commentaire n'a pas de valeur pour un champ, le corps contient null (ou false pour les booléens et [] pour les listes), de sorte que la forme d'une livraison ne varie jamais d'un commentaire à l'autre.

L'objet 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

Lorsque des utilisateurs sont mentionnés dans un commentaire, l'information est stockée dans une liste appelée mentions. Chaque objet de cette liste possède la structure suivante.

L'objet 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

Chaque entrée de la liste badges possède la structure suivante. Consultez le guide des badges pour les types de badges.

L'objet 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

Méthodes HTTP

Vous pouvez configurer la méthode HTTP pour chaque type d'événement webhook dans le panneau d'administration :

  • Événement de création : POST ou PUT (par défaut : PUT)
  • Événement de mise à jour : POST ou PUT (par défaut : PUT)
  • Événement de suppression : DELETE, POST ou PUT (par défaut : DELETE)

Comme toutes les requêtes contiennent un ID, les opérations de création et de mise à jour sont idempotentes par défaut (PUT). Répéter la même requête de création ou de mise à jour ne doit pas créer d'objets en double de votre côté.

En‑têtes de requête

Chaque requête webhook inclut les en‑têtes suivants :

En‑têteDescription
Content-Typeapplication/json
tokenVotre secret d'API
X-FastComments-TimestampHorodatage Unix (secondes) au moment où la requête a été signée
X-FastComments-SignatureSignature HMAC‑SHA256 (sha256=<hex>)

Voir Sécurité & jetons d'API pour des informations sur la vérification de la signature HMAC.

Sécurité et jetons d'API Internal Link

Les requêtes webhook FastComments incluent plusieurs mécanismes d'authentification pour la sécurité.

En-têtes envoyés

En-têteDescription
tokenVotre Secret d'API (pour compatibilité ascendante)
X-FastComments-TimestampHorodatage Unix (secondes) indiquant quand la requête a été signée
X-FastComments-SignatureSignature HMAC-SHA256 de la charge utile

Vérification de la signature HMAC (recommandée)

Nous recommandons fortement de vérifier la signature HMAC pour garantir que les charges utiles des webhooks sont authentiques et n'ont pas été altérées.

Format de la signature : sha256=<hex-encoded-signature>

Comment la signature est calculée :

  1. Concaténez : timestamp + "." + JSON_payload_body
  2. Calculez le HMAC-SHA256 en utilisant votre Secret d'API comme clé
  3. Encodez le résultat en hexadécimal

Exemple de vérification (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;
    }

    // Vérifier que l'horodatage est récent (moins de 5 minutes)
    const now = Math.floor(Date.now() / 1000);
    if (Math.abs(now - parseInt(timestamp, 10)) > 300) {
        return false;  // Prévention des attaques par rejeu
    }

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

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

Exemple de vérification (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

    # Vérifier que l'horodatage est récent
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
        return False

    # Vérifier la signature
    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}"

Exemple de vérification (PHP)

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

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

    // Vérifier que l'horodatage est récent (moins de 5 minutes)
    $now = time();
    if (abs($now - intval($timestamp)) > 300) {
        return false;
    }

    // Vérifier la signature
    $payload = json_encode($body, JSON_UNESCAPED_SLASHES);
    $message = $timestamp . '.' . $payload;
    $expectedSignature = 'sha256=' . hash_hmac('sha256', $message, $apiSecret);

    return hash_equals($expectedSignature, $signature);
}

Authentification héritée

L'en-tête token contenant votre Secret d'API est toujours envoyé pour assurer la compatibilité ascendante. Cependant, nous recommandons de migrer vers la vérification HMAC pour une sécurité renforcée, car elle protège contre les attaques par rejeu.

Gestion des webhooks via l'API Internal Link

Webhooks peuvent également être gérés via l'API REST. C’est ainsi que des intégrations comme Zapier s’abonnent aux événements de commentaires sans toucher au tableau de bord, et cela suit le modèle REST Hooks : s’abonner, recevoir des événements, se désabonner.

Les abonnements API coexistent avec les webhooks configurés dans le tableau de bord. Un événement de commentaire est livré à chaque webhook dont le domaine correspond, chacun sous forme de livraison distincte, quel que soit le mode de création du webhook.

Authentification

Chaque requête doit inclure votre clé API dans l’en-tête x-api-key (ou le paramètre de requête API_KEY) ainsi que votre ID de locataire dans le paramètre de requête tenantId. Les deux sont affichés sur la page API Secret du tableau de bord.

Souscrire

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"
}
ChampObligatoireDescription
urlYesUne URL http ou https absolue.
eventYescomment-created, comment-updated ou comment-deleted.
domainNoUn domaine provenant de la configuration de votre compte. La valeur par défaut est *, qui reçoit les événements pour tous les domaines.
methodNoPOST (par défaut), PUT ou DELETE.

La réponse contient l’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"
    }
}

S’abonner à la même URL pour le même événement et le même domaine renvoie à nouveau l’abonnement existant au lieu de créer un doublon, ce qui permet à un client de réessayer en toute sécurité. Chaque locataire peut avoir jusqu’à 50 abonnements API.

Lister

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

Renvoie tous les webhooks du locataire, y compris ceux gérés dans le tableau de bord ("source": "dashboard"). Filtrez avec event, domain ou source.

Se désabonner

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

Supprimer un abonnement supprime également tous les événements encore en file d’attente pour celui‑ci. Seuls les abonnements créés via l’API peuvent être supprimés de cette manière ; un webhook du tableau de bord, ou un identifiant qui n’existe pas sur votre compte, renvoie 404 avec le code not-found. Les webhooks du tableau de bord sont modifiés sur la page Webhooks.

Charges utiles et signature

Les livraisons utilisent la même charge utile que les webhooks du tableau de bord (voir Structures de données) et sont signées avec le même schéma HMAC (voir Sécurité & jetons API). Les abonnements API ne reçoivent jamais l’en‑tête hérité token, il faut donc vérifier l’en‑tête X-FastComments-Signature à la place.

Exemples de charges utiles

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

Renvoie les commentaires les plus récents du compte exactement dans la forme qu’une livraison transporte, afin qu’une intégration puisse afficher de vraies données d’exemple avant l’arrivée du premier événement. event est optionnel et uniquement validé, puisque chaque événement délivre le même objet commentaire. limit vaut par défaut 3 et accepte de 1 à 10. Coûte 2 crédits 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
        }
    ]
}

Répondre avec 410 Gone

Si le point de terminaison d’un abonnement API répond avec HTTP 410 Gone, FastComments considère cela comme un désabonnement : l’abonnement est supprimé ainsi que ses événements en file d’attente, et aucune autre livraison n’est tentée. Les webhooks configurés dans le tableau de bord ne sont jamais supprimés automatiquement ; pour eux, un 410 représente simplement un échec. Tout autre statut d’échec est réessayé et finit par désactiver le webhook, comme décrit dans Fonctionnement & Gestion des nouvelles tentatives.

Tableau de bord

Les abonnements API apparaissent dans la liste des Webhooks avec la source API, où un administrateur peut les modifier, les désactiver, les réactiver ou les supprimer.

En conclusion

Cela conclut notre documentation sur les Webhooks.

Nous espérons que vous trouverez l'intégration Webhook de FastComments facile à comprendre et rapide à configurer.

Si vous pensez avoir identifié des lacunes dans notre documentation, faites-nous savoir ci-dessous.