FastComments.com


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 oferece suporte a webhooks apenas para o recurso Comment.

Suportamos webhooks para criação, remoção e atualização de comentários.

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

Testes Internal Link

Na administração de Webhooks existem botões Send Test Payload para cada tipo de evento (Create, Update, Delete). Os eventos Create e Update enviam um objeto fictício WebhookComment, enquanto o teste de Delete enviará um corpo de requisição fictício contendo apenas um ID.

Verificando Payloads

Ao testar sua integração de webhook, verifique se as requisições recebidas incluem os seguintes cabeçalhos:

  1. token - Your API Secret
  2. X-FastComments-Timestamp - Unix timestamp (seconds)
  3. X-FastComments-Signature - HMAC-SHA256 signature

Use a verificação de assinatura HMAC para garantir que os payloads sejam autênticos.

Ferramentas de Teste

Você pode usar ferramentas como webhook.site ou ngrok para inspecionar os payloads de webhook recebidos durante o desenvolvimento.

Tipos de Evento

  • Create Event: Acionado quando um novo comentário é criado. Método padrão: PUT
  • Update Event: Acionado quando um comentário é editado. Método padrão: PUT
  • Delete Event: Acionado quando um comentário é excluído. Método padrão: DELETE

Cada evento inclui todos os dados do comentário no corpo da requisição (veja Estruturas de Dados para o formato do payload).

Estruturas de dados Internal Link

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

A Estrutura do Objeto WebhookComment

A Estrutura do Evento "Create"

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

A Estrutura do Evento "Update"

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

A Estrutura do Evento "Delete"

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

Mudança 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.
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
9 /** O id do usuário que deixou o comentário. Se SSO, prefixado com tenant id. **/
10 userId?: string
11 /** O email do usuário que deixou o comentário. **/
12 commenterEmail?: string
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
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 /** True se o usuário estava logado quando comentou, ou se verificou o comentário, ou se verificou sua sessão quando o comentário foi deixado. **/
30 verified: boolean
31 /** Data quando o comentário foi verificado. **/
32 verifiedDate?: number
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
37 /** O comentário foi marcado manualmente 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 em que o comentário está para a direção de ordenação "Most Relevant". **/
44 pageNumber: number
45 /** O número da página em que o comentário está para a direção de ordenação "Oldest First". **/
46 pageNumberOF: number
47 /** O número da página em que o comentário está para a direção de ordenação "Newest First". **/
48 pageNumberNF: number
49 /** O comentário foi aprovado automaticamente 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
53 /** As @menções escritas no comentário que foram parseadas com sucesso. **/
54 mentions?: CommentUserMention[]
55 /** O domínio de onde o comentário veio. **/
56 domain?: string
57 /** A lista opcional de ids de grupos de moderação associados a este comentário. **/
58 moderationGroupIds?: string[]|null
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 de Menções do Webhook
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** O id do usuário. Para usuários SSO, isso terá seu tenant id prefixado. **/
4 id: string
5 /** O texto final da @menção, incluindo o símbolo @. **/
6 tag: string
7 /** O texto original da @menção, incluindo o símbolo @. **/
8 rawTag: string
9 /** Que tipo de usuário foi marcado. user = FastComments.com account. sso = SSOUser. **/
10 type: 'user'|'sso'
11 /** Se o usuário optar por não receber notificações, isto 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 do 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, operações de Create e Update são idempotentes por padrão (PUT). Repetir a mesma requisição de Create ou Update não deve criar objetos duplicados do seu lado.

Cabeçalhos da Requisição

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

Header Descrição
Content-Type application/json
token Seu segredo de API
X-FastComments-Timestamp Timestamp Unix (segundos) quando a requisição foi assinada
X-FastComments-Signature Assinatura HMAC-SHA256 (sha256=<hex>)

Veja Security & API Tokens para informações sobre verificação da assinatura HMAC.

Segurança e tokens de API Internal Link

FastComments webhook requests include multiple authentication mechanisms for security.

Headers Sent

Header Description
token Seu API Secret (para compatibilidade com versões anteriores)
X-FastComments-Timestamp Timestamp Unix (segundos) quando a requisição foi assinada
X-FastComments-Signature Assinatura 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.

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.