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

La única estructura enviada vía webhooks es el objeto WebhookComment, descrito en TypeScript a continuación.

La estructura del objeto WebhookComment

La estructura del evento "Create"

El cuerpo de la solicitud del evento "create" es un objeto WebhookComment.

La estructura del evento "Update"

El cuerpo de la solicitud del evento "update" es un objeto WebhookComment.

La estructura del evento "Delete"

El cuerpo de la solicitud del evento "delete" es un objeto WebhookComment.

Cambio a partir del 14 de noviembre de 2023
Anteriormente, el cuerpo de la solicitud del evento "delete" solo contenía el id del comentario. Ahora contiene el comentario completo en el momento de la eliminación.

Cada clave está siempre presente en el cuerpo. Cuando el comentario no tiene valor para un campo, el cuerpo lleva null (o false para booleanos y [] para listas), por lo que la forma de una entrega nunca varía de un comentario a otro.

El objeto 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

Cuando se etiqueta a usuarios en un comentario, la información se almacena en una lista llamada mentions. Cada objeto de esa lista tiene la siguiente estructura.

El objeto 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

Cada entrada de la lista badges tiene la siguiente estructura. Consulta la guía de Badges para conocer los tipos de badges.

El objeto 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étodos HTTP

Puedes configurar el método HTTP para cada tipo de evento de webhook en el panel de administración:

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

Dado que todas las solicitudes contienen un ID, las operaciones Create y Update son idempotentes por defecto (PUT). Repetir la misma solicitud Create o Update no debería crear objetos duplicados en tu lado.

Cabeceras de la solicitud

Cada solicitud de webhook incluye las siguientes cabeceras:

CabeceraDescripción
Content-Typeapplication/json
tokenTu API Secret
X-FastComments-TimestampMarca de tiempo Unix (segundos) cuando se firmó la solicitud
X-FastComments-SignatureFirma HMAC-SHA256 (sha256=<hex>)

Consulta Seguridad y tokens de API para obtener información sobre cómo verificar la firma HMAC.

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
urlSíUna URL http o https absoluta.
eventSícomment-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.