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

No painel de 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 dummy WebhookComment, enquanto testar Delete enviará um corpo de requisição dummy com 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 - Seu Segredo da API
  2. X-FastComments-Timestamp - Marca de tempo Unix (segundos)
  3. X-FastComments-Signature - assinatura HMAC-SHA256

Use a verificação da assinatura HMAC para garantir que os payloads são 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 Eventos

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

Cada evento inclui os dados completos 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.

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 novembro de 2023
Anteriormente, o corpo da requisição do evento "delete" continha apenas o id do comentário. Agora ele 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 tópico de comentários. Normalizado. **/
6 urlId: string
7 /** A URL que aponta para onde o comentário foi feito. **/
8 url?: string
9 /** O id do usuário que deixou o comentário. Se SSO, prefixado com o id do tenant. **/
10 userId?: string
11 /** O email do usuário que deixou o comentário. **/
12 commenterEmail?: string
13 /** O nome do usuário exibido no widget de comentários. 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 em UTC quando o comentário foi feito. **/
24 date: UTC_ISO_DateString
25 /** Karma combinado (positivos - negativos) dos votos. **/
26 votes: number
27 votesUp: number
28 votesDown: number
29 /** Verdadeiro se o usuário estava logado quando comentou, ou se ele verificou o comentário, ou se verificou a sessão quando o comentário foi feito. **/
30 verified: boolean
31 /** Data em que 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 apenas 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 ordenação "Most Relevant". **/
44 pageNumber: number
45 /** O número da página em que o comentário está para a ordenação "Oldest First". **/
46 pageNumberOF: number
47 /** O número da página em que o comentário está para a 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 @mentions escritas no comentário que foram parseadas com sucesso. **/
54 mentions?: CommentUserMention[]
55 /** O domínio de onde o comentário vem. **/
56 domain?: string
57 /** A lista opcional de ids de grupo de moderação associados a este comentário. **/
58 moderationGroupIds?: string[]|null
59}
60

Quando usuários são marcados em um comentário, as informações são armazenadas 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á o id do seu 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 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 "create": POST ou PUT (padrão: PUT)
  • Evento "update": POST ou PUT (padrão: PUT)
  • Evento "delete": DELETE, POST ou PUT (padrão: DELETE)

Como todas as requisições contêm um ID, as operações 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 do webhook inclui os seguintes cabeçalhos:

Header Description
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 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

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.