FastComments.com

Webhooks


Com o FastComments é possível invocar um endpoint de API sempre que um comentário for adicionado, atualizado ou removido do nosso sistema.

Fazemos isso com webhooks assíncronos sobre HTTP/HTTPS.

O que são Webhooks Internal Link


Um Webhook é um mecanismo, ou uma integração, entre dois sistemas em que o "produtor" (FastComments) dispara um evento que o "consumidor" (você) consome por meio de uma chamada de API.


Eventos e Recursos Suportados Internal Link

FastComments suporta webhooks apenas para o recurso Comentário.

Nós suportamos webhooks para criação, remoção e atualização de comentários.

Cada um desses é considerado um evento separado em nosso sistema e, como tal, possui semânticas e estruturas diferentes para os eventos de webhook.

Qualquer número de endpoints pode se inscrever no mesmo evento, a partir do painel ou através da API (veja Gerenciando Webhooks via API). Cada webhook é entregue de forma independente.

Teste Internal Link

The new and edit webhook pages have a Send Test Payload button that sends a request to the URL currently in the form, whether or not it has been saved. The Create and Update events send a dummy WebhookComment object, while testing Delete will send a dummy request body with just an ID.

Verificando Payloads

When testing your webhook integration, verify the incoming requests include the following headers:

  1. X-FastComments-Timestamp - Unix timestamp (seconds)
  2. X-FastComments-Signature - HMAC-SHA256 signature

Webhooks created before the signature scheme was introduced also receive a token header containing your API Secret. New webhooks do not.

Use the HMAC signature verification to ensure payloads are authentic.

Ferramentas de Teste

You can use tools like webhook.site or ngrok to inspect incoming webhook payloads during development.

Tipos de Evento

  • Create Event: Triggered when a new comment is created.
  • Update Event: Triggered when a comment is edited.
  • Delete Event: Triggered when a comment is deleted.

Each webhook is tied to one event and one HTTP method (POST, PUT or DELETE). Each event includes the full comment data in the request body (see Data Structures for the payload format).

Estruturas de Dados Internal Link

A única estrutura enviada via webhooks é o objeto WebhookComment, descrito em TypeScript abaixo.

Estrutura do Objeto WebhookComment

Estrutura do Evento "Create"

O corpo da requisição do evento "create" é um objeto WebhookComment.

Estrutura do Evento "Update"

O corpo da requisição do evento "update" é um objeto WebhookComment.

Estrutura do Evento "Delete"

O corpo da requisição do evento "delete" é um objeto WebhookComment.

Alteração a partir de 14 de nov de 2023
Anteriormente, o corpo da requisição do evento "delete" continha apenas o id do comentário. Agora contém o comentário completo no momento da exclusão.

Cada chave está sempre presente no corpo. Quando o comentário não tem valor para um campo, o corpo contém null (ou false para booleanos e [] para listas), de modo que a estrutura de entrega nunca varia de um comentário para outro.

O Objeto WebhookComment
Copy CopyRun External Link
1
2interface WebhookComment {
3 /** O id do comentário. **/
4 id: string
5 /** O id ou URL que identifica o thread do comentário. Normalizado. **/
6 urlId: string
7 /** A URL que aponta para onde o comentário foi deixado. **/
8 url: string | null
9 /** O id do usuário que deixou o comentário. Se SSO, prefixado com o id do tenant. **/
10 userId: string | null
11 /** O email do usuário que deixou o comentário. **/
12 commenterEmail: string | null
13 /** O nome do usuário que aparece no widget de comentário. Com SSO, pode ser displayName. **/
14 commenterName: string
15 /** Texto bruto do comentário. **/
16 comment: string
17 /** Texto do comentário após o parsing. **/
18 commentHTML: string
19 /** Id externo do comentário. **/
20 externalId: string | null
21 /** O id do comentário pai. **/
22 parentId: string | null
23 /** A data UTC quando o comentário foi deixado. **/
24 date: UTC_ISO_DateString
25 /** Karma combinado (up - down) dos votos. **/
26 votes: number
27 votesUp: number
28 votesDown: number
29 /** Verdadeiro se o usuário estava logado ao comentar, ou verificou o comentário, ou se verificou a sessão quando o comentário foi deixado. **/
30 verified: boolean
31 /** A data UTC quando o comentário foi verificado. **/
32 verifiedDate: UTC_ISO_DateString | null
33 /** Se um moderador marcou o comentário como revisado. **/
34 reviewed: boolean
35 /** A localização, ou codificação base64, do avatar. Será base64 somente se esse foi o valor passado com SSO. **/
36 avatarSrc: string | null
37 /** O comentário foi marcado manual ou automaticamente como spam? **/
38 isSpam: boolean
39 /** O comentário foi marcado automaticamente como spam? **/
40 aiDeterminedSpam: boolean
41 /** Existem imagens no comentário? **/
42 hasImages: boolean
43 /** O número da página onde o comentário está para a ordenação "Mais Relevante". **/
44 pageNumber: number | null
45 /** O número da página onde o comentário está para a ordenação "Mais Antigos Primeiro". **/
46 pageNumberOF: number | null
47 /** O número da página onde o comentário está para a ordenação "Mais Recentes Primeiro". **/
48 pageNumberNF: number | null
49 /** O comentário foi aprovado automática ou manualmente? **/
50 approved: boolean
51 /** O código de localidade (formato: en_us) do usuário quando o comentário foi escrito. **/
52 locale: string | null
53 /** As @menções escritas no comentário que foram analisadas com sucesso. Vazio quando não houver nenhuma. **/
54 mentions: CommentUserMention[]
55 /** O domínio de onde o comentário vem. **/
56 domain: string | null
57 /** Os ids dos grupos de moderação associados a este comentário. Vazio quando não houver nenhum. **/
58 moderationGroupIds: string[]
59}
60

Quando usuários são marcados em um comentário, a informação é armazenada em uma lista chamada mentions. Cada objeto nessa lista tem a seguinte estrutura.

O Objeto Webhook Mentions
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** O id do usuário. Para usuários SSO, este terá o id do tenant prefixado. **/
4 id: string
5 /** O texto final da tag @mention, incluindo o símbolo @. **/
6 tag: string
7 /** O texto original da tag @mention, incluindo o símbolo @. **/
8 rawTag: string
9 /** Que tipo de usuário foi marcado. user = conta FastComments.com. sso = SSOUser. **/
10 type: 'user'|'sso'
11 /** Se o usuário optou por não receber notificações, isso ainda será definido como true. **/
12 sent: boolean
13}
14

Métodos HTTP

Você pode configurar o método HTTP para cada tipo de evento webhook no painel de administração:

  • Evento de Criação: POST ou PUT (padrão: PUT)
  • Evento de Atualização: POST ou PUT (padrão: PUT)
  • Evento de Exclusão: DELETE, POST ou PUT (padrão: DELETE)

Como todas as requisições contêm um ID, as operações de Criação e Atualização são idempotentes por padrão (PUT). Repetir a mesma requisição de Criação ou Atualização não deve criar objetos duplicados no seu lado.

Cabeçalhos da Requisição

Cada requisição webhook inclui os seguintes cabeçalhos:

HeaderDescription
Content-Typeapplication/json
tokenSeu segredo da API
X-FastComments-TimestampTimestamp Unix (segundos) quando a requisição foi assinada
X-FastComments-SignatureAssinatura HMAC-SHA256 (sha256=<hex>)

Veja Segurança e Tokens de API para informações sobre como verificar a assinatura HMAC.

Segurança e Tokens de API Internal Link

FastComments webhook requests include multiple authentication mechanisms for security.

Headers Sent

HeaderDescription
tokenSeu API Secret (para compatibilidade com versões anteriores)
X-FastComments-TimestampTimestamp Unix (segundos) quando a requisição foi assinada
X-FastComments-SignatureAssinatura HMAC-SHA256 do payload

Recomendamos fortemente verificar a assinatura HMAC para garantir que os payloads do webhook sejam autênticos e não tenham sido adulterados.

Signature Format: sha256=<hex-encoded-signature>

How the signature is computed:

  1. Concatenate: timestamp + "." + JSON_payload_body
  2. Compute HMAC-SHA256 using your API Secret as the key
  3. Hex-encode the result

Example Verification (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 se o timestamp é recente (dentro de 5 minutos)
    const now = Math.floor(Date.now() / 1000);
    if (Math.abs(now - parseInt(timestamp, 10)) > 300) {
        return false;  // Prevenção contra replay
    }

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

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

Example Verification (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 se o timestamp é recente
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
        return False

    # Verifica a assinatura
    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}"

Example Verification (PHP)

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

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

    // Verifica se o timestamp é recente (dentro de 5 minutos)
    $now = time();
    if (abs($now - intval($timestamp)) > 300) {
        return false;
    }

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

    return hash_equals($expectedSignature, $signature);
}

Legacy Authentication

O cabeçalho token contendo seu API Secret ainda é enviado para compatibilidade com versões anteriores. No entanto, recomendamos migrar para a verificação HMAC para melhorar a segurança, pois isso protege contra ataques de replay.

Gerenciando Webhooks pela API Internal Link

Webhooks também podem ser gerenciados através da API REST. É assim que integrações como o Zapier se inscrevem em eventos de comentário sem tocar no painel, e segue o padrão REST Hooks: inscrever, receber eventos, cancelar inscrição.

As inscrições de API convivem ao lado dos webhooks configurados no painel. Um evento de comentário é entregue a cada webhook que corresponde ao seu domínio, cada um como sua própria entrega, independentemente de como o webhook foi criado.

Autenticação

Cada requisição precisa da sua API Key no cabeçalho x-api-key (ou no parâmetro de consulta API_KEY) e do seu ID de locatário no parâmetro de consulta tenantId. Ambos são exibidos na página API Secret no painel.

Inscrever-se

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"
}
CampoObrigatórioDescrição
urlSimUm URL http ou https absoluto.
eventSimcomment-created, comment-updated ou comment-deleted.
domainNãoUm domínio da configuração da sua conta. O padrão é *, que recebe eventos para todos os domínios.
methodNãoPOST (padrão), PUT ou DELETE.

A resposta contém a inscrição:

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

Inscrever a mesma URL no mesmo evento e domínio novamente retorna a inscrição existente em vez de criar um duplicado, permitindo que o cliente tente novamente com segurança. Cada locatário pode ter até 50 inscrições de API.

Listar

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

Retorna todos os webhooks do locatário, incluindo os gerenciados no painel (\"source\": \"dashboard\"). Filtre com event, domain ou source.

Cancelar inscrição

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

Excluir uma inscrição também descarta quaisquer eventos ainda enfileirados para ela. Apenas inscrições criadas através da API podem ser excluídas desta forma; um webhook do painel, ou um id que não exista na sua conta, responde 404 com o código not-found. Webhooks do painel são editados na página Webhooks.

Payloads e assinatura

Entregas usam o mesmo payload dos webhooks do painel (veja Estruturas de Dados) e são assinadas com o mesmo esquema HMAC (veja Segurança & Tokens de API). Inscrições de API nunca recebem o cabeçalho legado token, portanto verifique o cabeçalho X-FastComments-Signature.

Payloads de exemplo

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

Retorna os comentários mais recentes da conta exatamente no formato que uma entrega transporta, permitindo que uma integração mostre dados de exemplo reais antes que o primeiro evento chegue. event é opcional e apenas validado, já que todo evento entrega o mesmo objeto de comentário. limit tem padrão 3 e aceita de 1 a 10. Custa 2 créditos de 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
        }
    ]
}

Respondendo com 410 Gone

Se o endpoint de uma inscrição de API responder com HTTP 410 Gone, o FastComments trata isso como um cancelamento de inscrição: a inscrição é excluída junto com seus eventos enfileirados, e nenhuma entrega adicional é tentada. Webhooks configurados no painel nunca são excluídos automaticamente; para eles um 410 é uma falha comum. Qualquer outro status de falha é refeito e eventualmente desabilita o webhook, conforme descrito em Como funciona & Tratamento de Repetições.

Painel

Inscrições de API aparecem na lista de Webhooks com a origem API, onde um administrador pode editar, desativar, reativar ou excluí‑las.

Conclusão

Isto conclui nossa documentação sobre Webhooks.

Esperamos que você ache a integração de Webhooks do FastComments fácil de entender e rápida de configurar.

Se você sentir que identificou alguma lacuna em nossa documentação, informe-nos abaixo.