FastComments.com

Webhooks


Con FastComments es posible invocar un endpoint de la API cada vez que se añade, actualiza o elimina un comentario de nuestro sistema.

Lo logramos con webhooks asíncronos a través de HTTP/HTTPS.


Qué son los Webhooks Internal Link


Un Webhook es un mecanismo, o una integración, entre dos sistemas donde el "productor" (FastComments) desencadena un evento que el "consumidor" (Usted) consume mediante una llamada a la API.


Eventos y recursos compatibles Internal Link

FastComments admite webhooks solo para el recurso Comentario.

Admitimos webhooks para la creación, eliminación y actualización de comentarios.

Cada uno de estos se considera un evento separado en nuestro sistema y, como tal, tiene diferentes semánticas y estructuras para los eventos de webhook.

Cualquier número de endpoints puede suscribirse al mismo evento, desde el panel de control o a través de la API (vea Administrar Webhooks a través de la API). Cada webhook se entrega de forma independiente.

Pruebas Internal Link

Las nuevas páginas de webhook y de edición tienen un botón Send Test Payload que envía una solicitud a la URL que está actualmente en el formulario, haya sido guardada o no. Los eventos Create y Update envían un objeto WebhookComment de prueba, mientras que al probar Delete se enviará un cuerpo de solicitud de prueba con solo un ID.

Verificando Cargas Útiles

Al probar su integración de webhook, verifique que las solicitudes entrantes incluyan los siguientes encabezados:

  1. X-FastComments-Timestamp - Marca de tiempo Unix (segundos)
  2. X-FastComments-Signature - Firma HMAC‑SHA256

Los webhooks creados antes de que se introdujera el esquema de firma también reciben un encabezado token que contiene su API Secret. Los webhooks nuevos no lo hacen.

Utilice la verificación de firma HMAC para garantizar que las cargas sean auténticas.

Herramientas de Prueba

Puede usar herramientas como webhook.site o ngrok para inspeccionar las cargas de webhook entrantes durante el desarrollo.

Tipos de Eventos

  • Create Event: Se dispara cuando se crea un nuevo comentario.
  • Update Event: Se dispara cuando se edita un comentario.
  • Delete Event: Se dispara cuando se elimina un comentario.

Cada webhook está asociado a un evento y a un método HTTP (POST, PUT o DELETE). Cada evento incluye los datos completos del comentario en el cuerpo de la solicitud (vea Data Structures para el formato de la carga).


Estructuras de datos 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.

El objeto WebhookComment
Copy CopyRun External Link
1
2interface WebhookComment {
3 /** El id del comentario. **/
4 id: string
5 /** El id o URL que identifica el hilo del comentario. Normalizado. **/
6 urlId: string
7 /** La URL que apunta a donde se dejó el comentario. **/
8 url: string | null
9 /** El id de usuario que dejó el comentario. Si SSO, con prefijo del id del inquilino. **/
10 userId: string | null
11 /** El correo electrónico del usuario que dejó el comentario. **/
12 commenterEmail: string | null
13 /** El nombre del usuario que se muestra en el widget de comentarios. Con SSO, puede ser displayName. **/
14 commenterName: string
15 /** Texto bruto del comentario. **/
16 comment: string
17 /** Texto del comentario después del parseo. **/
18 commentHTML: string
19 /** Id externo del comentario. **/
20 externalId: string | null
21 /** El id del comentario padre. **/
22 parentId: string | null
23 /** La fecha UTC cuando se dejó el comentario. **/
24 date: UTC_ISO_DateString
25 /** Karma combinado (up - down) de los votos. **/
26 votes: number
27 votesUp: number
28 votesDown: number
29 /** true si el usuario estaba conectado cuando comentó, o verificó el comentario, o si verificó su sesión cuando se dejó el comentario. **/
30 verified: boolean
31 /** La fecha UTC cuando el comentario fue verificado. **/
32 verifiedDate: UTC_ISO_DateString | null
33 /** Si un moderador marcó el comentario como revisado. **/
34 reviewed: boolean
35 /** La ubicación, o codificación base64, del avatar. Solo será base64 si ese fue el valor pasado con SSO. **/
36 avatarSrc: string | null
37 /** ¿Fue el comentario marcado manual o automáticamente como spam? **/
38 isSpam: boolean
39 /** ¿Fue el comentario marcado automáticamente como spam? **/
40 aiDeterminedSpam: boolean
41 /** ¿Hay imágenes en el comentario? **/
42 hasImages: boolean
43 /** El número de página en la que se encuentra el comentario para la dirección de ordenación "Most Relevant". **/
44 pageNumber: number | null
45 /** El número de página en la que se encuentra el comentario para la dirección de ordenación "Oldest First". **/
46 pageNumberOF: number | null
47 /** El número de página en la que se encuentra el comentario para la dirección de ordenación "Newest First". **/
48 pageNumberNF: number | null
49 /** ¿Fue el comentario aprobado automáticamente o manualmente? **/
50 approved: boolean
51 /** El código de locale (formato: en_us) del usuario cuando se escribió el comentario. **/
52 locale: string | null
53 /** Las @mentions escritas en el comentario que fueron parseadas exitosamente. Vacío cuando no hay ninguna. **/
54 mentions: CommentUserMention[]
55 /** El dominio del que proviene el comentario. **/
56 domain: string | null
57 /** Los ids de los grupos de moderación asociados a este comentario. Vacío cuando no hay ninguno. **/
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.

El objeto Webhook Mentions
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** El id de usuario. Para usuarios SSO, tendrá el id del inquilino como prefijo. **/
4 id: string
5 /** El texto final de la etiqueta @mention, incluyendo el símbolo @. **/
6 tag: string
7 /** El texto original de la etiqueta @mention, incluyendo el símbolo @. **/
8 rawTag: string
9 /** Qué tipo de usuario fue etiquetado. user = cuenta de FastComments.com. sso = SSOUser. **/
10 type: 'user'|'sso'
11 /** Si el usuario opta por no recibir notificaciones, esto seguirá siendo true. **/
12 sent: boolean
13}
14

HTTP Methods

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

  • Evento Create: POST o PUT (por defecto: PUT)
  • Evento Update: POST o PUT (por defecto: PUT)
  • Evento Delete: DELETE, POST o PUT (por defecto: 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
tokenTu secreto de API
X-FastComments-TimestampMarca de tiempo Unix (segundos) cuando la solicitud fue firmada
X-FastComments-SignatureHMAC-SHA256 signature (sha256=<hex>)

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

Seguridad y tokens de API Internal Link


Las solicitudes webhook de FastComments incluyen múltiples mecanismos de autenticación por motivos de seguridad.

Encabezados enviados

HeaderDescription
tokenTu API Secret (para compatibilidad con versiones anteriores)
X-FastComments-TimestampMarca de tiempo Unix (segundos) cuando se firmó la solicitud
X-FastComments-SignatureFirma HMAC-SHA256 de la carga útil

Verificación de firma HMAC (Recomendado)

Recomendamos encarecidamente verificar la firma HMAC para garantizar que las cargas útiles de los webhooks sean auténticas y no hayan sido manipuladas.

Formato de la firma: sha256=<hex-encoded-signature>

Cómo se calcula la firma:

  1. Concatenar: timestamp + "." + JSON_payload_body
  2. Calcular HMAC-SHA256 usando tu API Secret como clave
  3. Codificar el resultado en hexadecimal

Ejemplo de verificación (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;
    }

    // Verificar que la marca de tiempo sea reciente (dentro de 5 minutos)
    const now = Math.floor(Date.now() / 1000);
    if (Math.abs(now - parseInt(timestamp, 10)) > 300) {
        return false;  // Prevención de ataques de repetición
    }

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

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

Ejemplo de verificación (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

    # Verificar que la marca de tiempo sea reciente
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
        return False

    # Verificar 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}"

Ejemplo de verificación (PHP)

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

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

    // Verificar que la marca de tiempo sea reciente (dentro de 5 minutos)
    $now = time();
    if (abs($now - intval($timestamp)) > 300) {
        return false;
    }

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

    return hash_equals($expectedSignature, $signature);
}

Autenticación heredada

El encabezado token que contiene tu API Secret todavía se envía por compatibilidad con versiones anteriores. Sin embargo, recomendamos migrar a la verificación HMAC para mejorar la seguridad, ya que protege contra ataques de repetición.


Gestión de Webhooks a través de la API Internal Link

Webhooks también pueden gestionarse a través de la API REST. Así es como integraciones como Zapier se suscriben a eventos de comentarios sin tocar el panel de control, y sigue el patrón REST Hooks: suscribirse, recibir eventos, cancelar la suscripción.

Las suscripciones API conviven con los webhooks configurados en el panel de control. Un evento de comentario se entrega a cada webhook que coincida con su dominio, cada uno como una entrega independiente, sin importar cómo se haya creado el webhook.

Autenticación

Cada solicitud necesita su API Key en el encabezado x-api-key (o el parámetro de consulta API_KEY) y su ID de inquilino en el parámetro de consulta tenantId. Ambos se muestran en la página API Secret del panel de control.

Suscripción

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"
}
CampoObligatorioDescripción
urlUna URL http o https absoluta.
eventcomment-created, comment-updated o comment-deleted.
domainNoUn dominio de la configuración de su cuenta. Por defecto es *, que recibe eventos para todos los dominios.
methodNoPOST (por defecto), PUT o DELETE.

La respuesta contiene la suscripción:

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

Suscribir la misma URL al mismo evento y dominio nuevamente devuelve la suscripción existente en lugar de crear un duplicado, por lo que un cliente puede reintentar de forma segura. Cada inquilino puede tener hasta 50 suscripciones API.

Listado

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

Devuelve todos los webhooks del inquilino, incluidos los gestionados en el panel de control ("source": "dashboard").
Filtre con event, domain o source.

Cancelar suscripción

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

Eliminar una suscripción también descarta cualquier evento que aún esté en cola para ella. Solo las suscripciones creadas a través de la API pueden eliminarse de esta manera; un webhook del panel de control, o un id que no exista en su cuenta, responde con 404 y el código not-found. Los webhooks del panel de control se editan en la página Webhooks.

Cargas útiles y firma

Las entregas utilizan la misma carga útil que los webhooks del panel de control (ver Estructuras de datos) y están firmadas con el mismo esquema HMAC (ver Seguridad y tokens API). Las suscripciones API nunca reciben el encabezado token heredado, por lo que debe verificar el encabezado X-FastComments-Signature en su lugar.

Cargas útiles de ejemplo

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

Devuelve los comentarios más recientes de la cuenta con exactamente la forma que lleva una entrega, de modo que una integración pueda mostrar datos de muestra reales antes de que llegue el primer evento. event es opcional y solo se valida, ya que cada evento entrega el mismo objeto de comentario. limit por defecto es 3 y acepta valores de 1 a 10. Cuesta 2 créditos 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
        }
    ]
}

Responder con 410 Gone

Si el endpoint de una suscripción API responde con HTTP 410 Gone, FastComments lo trata como una cancelación de suscripción: la suscripción se elimina junto con sus eventos en cola, y no se intentan más entregas. Los webhooks configurados en el panel de control nunca se eliminan automáticamente; para ellos un 410 es un fallo ordinario. Cualquier otro estado de error se reintenta y eventualmente desactiva el webhook, como se describe en Cómo funciona y manejo de reintentos.

Panel de control

Las suscripciones API aparecen en la lista de Webhooks con la fuente API, donde un administrador puede editar, desactivar, volver a activar o eliminarlas.


En conclusión

Esto concluye nuestra documentación de Webhooks.

Esperamos que la integración de Webhooks de FastComments le resulte fácil de entender y rápida de configurar.

Si considera que ha identificado alguna laguna en nuestra documentación, háganoslo saber a continuación.