FastComments.com


ืขื FastComments ื ื™ืชืŸ ืœืงืจื•ื ืœื ืงื•ื“ืช ืงืฆื” ืฉืœ API ื‘ื›ืœ ืคืขื ืฉืชื’ื•ื‘ื” ืžืชื•ื•ืกืคืช, ืžืชืขื“ื›ื ืช ืื• ื ืžื—ืงืช ืžื”ืžืขืจื›ืช ืฉืœื ื•.

ืื ื• ืžืžืžืฉื™ื ื–ืืช ื‘ืืžืฆืขื•ืช webhooks ืืกื™ื ื›ืจื•ื ื™ื™ื ืขืœ ื’ื‘ื™ HTTP/HTTPS.


ืžื” ื”ื ื•ื•ื‘ึพื”ื•ืงื™ื Internal Link

Webhook ื”ื•ื ืžื ื’ื ื•ืŸ, ืื• ืื™ื ื˜ื’ืจืฆื™ื”, ื‘ื™ืŸ ืฉืชื™ ืžืขืจื›ื•ืช ืฉื‘ื” ื”"ืžืคื™ืง" (FastComments) ืฉื•ืœื— ืื™ืจื•ืข ืฉื”"ืฆืจื›ืŸ" (ืืชื”) ืฆื•ืจืš ื‘ืืžืฆืขื•ืช ืงืจื™ืืช API.

ืื™ืจื•ืขื™ื ื•ืžืฉืื‘ื™ื ื ืชืžื›ื™ื Internal Link

FastComments ืชื•ืžื›ืช ื‘-webhooks ืจืง ื‘ืžืฉืื‘ Comment.

ืื ื• ืชื•ืžื›ื™ื ื‘-webhooks ืขื‘ื•ืจ ื™ืฆื™ืจืช Comment, ื”ืกืจื” ื•ืขื“ ืขื“ื›ื•ืŸ.

ื›ืœ ืื—ื“ ืžื”ื ื ื—ืฉื‘ ืœืื™ืจื•ืข ื ืคืจื“ ื‘ืžืขืจื›ืช ืฉืœื ื• ื•ืœื›ืŸ ื™ืฉ ืœื• ืกืžื ื˜ื™ืงื” ืฉื•ื ื” ื•ืžื‘ื ื™ื ืฉื•ื ื™ื ืขื‘ื•ืจ ืื™ืจื•ืขื™ webhook.

ื‘ื“ื™ืงื•ืช Internal Link

ื‘ืžืžืฉืง ื ื™ื”ื•ืœ ื”-Webhooks ื™ืฉ ื›ืคืชื•ืจื™ Send Test Payload ืขื‘ื•ืจ ื›ืœ ืกื•ื’ ืื™ืจื•ืข (Create, Update, Delete). ืื™ืจื•ืขื™ Create ื•-Update ืฉื•ืœื—ื™ื ืื•ื‘ื™ื™ืงื˜ WebhookComment ืžื“ื•ืžื”, ื‘ืขื•ื“ ืฉื‘ื‘ื“ื™ืงืช Delete ื™ืฉืœื— ื’ื•ืฃ ื‘ืงืฉื” ืžื“ื•ืžื” ื”ืžื›ื™ืœ ืจืง ID.

ืื™ืžื•ืช ื”ืžื˜ืขื ื™ื

ื‘ื‘ื™ืฆื•ืข ื‘ื“ื™ืงื•ืช ืฉืœ ืฉื™ืœื•ื‘ ื”-webhook ืฉืœืš, ื•ื“ื ืฉื”ื‘ืงืฉื•ืช ื”ื ื›ื ืกื•ืช ื›ื•ืœืœื•ืช ืืช ื”ื›ื•ืชืจื•ืช ื”ื‘ืื•ืช:

  1. token - ืกื•ื“ ื”-API ืฉืœืš
  2. X-FastComments-Timestamp - ื—ื•ืชืžืช ื–ืžืŸ Unix (ื‘ืฉื ื™ื•ืช)
  3. X-FastComments-Signature - ื—ืชื™ืžืช HMAC-SHA256

ื”ืฉืชืžืฉ ื‘ืื™ืžื•ืช ื—ืชื™ืžืช HMAC ื›ื“ื™ ืœื•ื•ื“ื ืฉื”ืžื˜ืขื ื™ื ืื•ืชื ื˜ื™ื™ื.

ื›ืœื™ ื‘ื“ื™ืงื”

ื ื™ืชืŸ ืœื”ืฉืชืžืฉ ื‘ื›ืœื™ื ื›ืžื• webhook.site ืื• ngrok ื›ื“ื™ ืœื‘ื“ื•ืง ืืช ืžื˜ืขื ื™ ื”-webhook ื”ื ื›ื ืกื™ื ื‘ืžื”ืœืš ื”ืคื™ืชื•ื—.

ืกื•ื’ื™ ืื™ืจื•ืขื™ื

  • Create Event: ืžืชืจื—ืฉ ื›ืืฉืจ ืชื’ื•ื‘ื” ื—ื“ืฉื” ื ื•ืฆืจืช. ืฉื™ื˜ืช ื‘ืจื™ืจืช ืžื—ื“ืœ: PUT
  • Update Event: ืžืชืจื—ืฉ ื›ืืฉืจ ืชื’ื•ื‘ื” ื ืขืจื›ืช. ืฉื™ื˜ืช ื‘ืจื™ืจืช ืžื—ื“ืœ: PUT
  • Delete Event: ืžืชืจื—ืฉ ื›ืืฉืจ ืชื’ื•ื‘ื” ื ืžื—ืงืช. ืฉื™ื˜ืช ื‘ืจื™ืจืช ืžื—ื“ืœ: DELETE

ื›ืœ ืื™ืจื•ืข ื›ื•ืœืœ ืืช ื ืชื•ื ื™ ื”ืชื’ื•ื‘ื” ื”ืžืœืื™ื ื‘ื’ื•ืฃ ื”ื‘ืงืฉื” (ืจืื” ืžื‘ื ื™ ื ืชื•ื ื™ื ืขื‘ื•ืจ ืคื•ืจืžื˜ ื”ืžื˜ืขืŸ).


ืžื‘ื ื™ ื ืชื•ื ื™ื Internal Link

ื”ืžื‘ื ื” ื”ื™ื—ื™ื“ ืฉื ืฉืœื— ื“ืจืš webhooks ื”ื•ื ื”ืื•ื‘ื™ื™ืงื˜ WebhookComment, ื”ืžืชื•ืืจ ื‘-TypeScript ืœืžื˜ื”.

ืžื‘ื ื” ื”-WebhookComment

ืžื‘ื ื” ื”ืื™ืจื•ืข "create"

ื’ื•ืฃ ื”ื‘ืงืฉื” ืฉืœ ืื™ืจื•ืข ื”-"create" ื”ื•ื ืื•ื‘ื™ื™ืงื˜ WebhookComment.

ืžื‘ื ื” ื”ืื™ืจื•ืข "update"

ื’ื•ืฃ ื”ื‘ืงืฉื” ืฉืœ ืื™ืจื•ืข ื”-"update" ื”ื•ื ืื•ื‘ื™ื™ืงื˜ WebhookComment.

ืžื‘ื ื” ื”ืื™ืจื•ืข "delete"

ื’ื•ืฃ ื”ื‘ืงืฉื” ืฉืœ ืื™ืจื•ืข ื”-"delete" ื”ื•ื ืื•ื‘ื™ื™ืงื˜ WebhookComment.

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.
ืื•ื‘ื™ื™ืงื˜ WebhookComment
Copy CopyRun External Link
1
2interface WebhookComment {
3 /** ื”-id ืฉืœ ื”ืชื’ื•ื‘ื”. **/
4 id: string
5 /** ื”-id ืื• ื”-URL ืฉืžื–ื”ื” ืืช ืฉืจืฉื•ืจ ื”ืชื’ื•ื‘ื•ืช. ืžื ื•ืจืžืœ. **/
6 urlId: string
7 /** ื”-URL ื”ืžืฆื‘ื™ืข ื”ื™ื›ืŸ ื”ื•ืฉืืจื” ื”ืชื’ื•ื‘ื”. **/
8 url?: string
9 /** ื”-id ืฉืœ ื”ืžืฉืชืžืฉ ืฉื”ืฉืื™ืจ ืืช ื”ืชื’ื•ื‘ื”. ืื SSO, ื™ื”ื™ื” ืขื ืงื™ื“ื•ืžืช ืžื–ื”ื” ื”-tenant. **/
10 userId?: string
11 /** ื”ื“ื•ื"ืœ ืฉืœ ื”ืžืฉืชืžืฉ ืฉื”ืฉืื™ืจ ืืช ื”ืชื’ื•ื‘ื”. **/
12 commenterEmail?: string
13 /** ื”ืฉื ืฉืœ ื”ืžืฉืชืžืฉ ืฉืžื•ืคื™ืข ื‘ื•ื•ื™ื“ื’'ื˜ ื”ืชื’ื•ื‘ื•ืช. ื‘-SSO, ื™ื›ื•ืœ ืœื”ื™ื•ืช displayName. **/
14 commenterName: string
15 /** ื˜ืงืกื˜ ื”ืชื’ื•ื‘ื” ื”ื’ื•ืœืžื™. **/
16 comment: string
17 /** ื˜ืงืกื˜ ื”ืชื’ื•ื‘ื” ืœืื—ืจ ืขื™ื‘ื•ื“. **/
18 commentHTML: string
19 /** id ื—ื™ืฆื•ื ื™ ืฉืœ ื”ืชื’ื•ื‘ื”. **/
20 externalId?: string
21 /** ื”-id ืฉืœ ืชื’ื•ื‘ืช ื”ืื‘. **/
22 parentId?: string | null
23 /** ืชืืจื™ืš UTC ืฉื‘ื• ื”ื•ืฉืืจื” ื”ืชื’ื•ื‘ื”. **/
24 date: UTC_ISO_DateString
25 /** ืงืืจืžื” ืžืฉื•ืœื‘ืช (up - down) ืฉืœ ื”ื”ืฆื‘ืขื•ืช. **/
26 votes: number
27 votesUp: number
28 votesDown: number
29 /** true ืื ื”ืžืฉืชืžืฉ ื”ื™ื” ืžื—ื•ื‘ืจ ื›ืฉื”ื’ื™ื‘, ืื• ืื ืื™ืžืช ืืช ื”ืชื’ื•ื‘ื”, ืื• ืื ืื™ืžืช ืืช ื”ืกืฉืŸ ืฉืœื• ื‘ืขืช ื”ืฉืืจืช ื”ืชื’ื•ื‘ื”. **/
30 verified: boolean
31 /** ื”ืชืืจื™ืš ืฉื‘ื• ื”ืชื’ื•ื‘ื” ืื•ืžืชื”. **/
32 verifiedDate?: number
33 /** ืื ืžื•ื“ืจื˜ื•ืจ ืกืžืŸ ืืช ื”ืชื’ื•ื‘ื” ื›ื ื‘ื“ืงื”. **/
34 reviewed: boolean
35 /** ื”ืžื™ืงื•ื, ืื• ืงื™ื“ื•ื“ base64, ืฉืœ ื”ืื•ื•ืื˜ืจ. ื™ื”ื™ื” base64 ืจืง ืื ื–ื• ื”ื™ื™ืชื” ื”ืขืจืš ืฉื ืฉืœื— ื‘-SSO. **/
36 avatarSrc?: string
37 /** ื”ืื ื”ืชื’ื•ื‘ื” ืกื•ืžื ื” ื›ืกืคืื ื™ื“ื ื™ืช ืื• ืื•ื˜ื•ืžื˜ื™ืช? **/
38 isSpam: boolean
39 /** ื”ืื ื”ืชื’ื•ื‘ื” ืกื•ืžื ื” ื›ืกืคืื ื‘ืื•ืคืŸ ืื•ื˜ื•ืžื˜ื™? **/
40 aiDeterminedSpam: boolean
41 /** ื”ืื ื™ืฉ ืชืžื•ื ื•ืช ื‘ืชื’ื•ื‘ื”? **/
42 hasImages: boolean
43 /** ืžืกืคืจ ื”ืขืžื•ื“ ืฉื‘ื• ื ืžืฆืืช ื”ืชื’ื•ื‘ื” ืขื‘ื•ืจ ื›ื™ื•ื•ืŸ ื”ืžื™ื•ืŸ "Most Relevant". **/
44 pageNumber: number
45 /** ืžืกืคืจ ื”ืขืžื•ื“ ืฉื‘ื• ื ืžืฆืืช ื”ืชื’ื•ื‘ื” ืขื‘ื•ืจ ื›ื™ื•ื•ืŸ ื”ืžื™ื•ืŸ "Oldest First". **/
46 pageNumberOF: number
47 /** ืžืกืคืจ ื”ืขืžื•ื“ ืฉื‘ื• ื ืžืฆืืช ื”ืชื’ื•ื‘ื” ืขื‘ื•ืจ ื›ื™ื•ื•ืŸ ื”ืžื™ื•ืŸ "Newest First". **/
48 pageNumberNF: number
49 /** ื”ืื ื”ืชื’ื•ื‘ื” ืื•ืฉืจื” ืื•ื˜ื•ืžื˜ื™ืช ืื• ื™ื“ื ื™ืช? **/
50 approved: boolean
51 /** ืงื•ื“ ื”ืฉืคื”/ืื–ื•ืจ (ืคื•ืจืžื˜: en_us) ืฉืœ ื”ืžืฉืชืžืฉ ื›ืืฉืจ ื ื›ืชื‘ื” ื”ืชื’ื•ื‘ื”. **/
52 locale: string
53 /** ื”-@mentions ืฉื ื›ืชื‘ื• ื‘ืชื’ื•ื‘ื” ื•ื ื•ืชื—ื• ื‘ื”ืฆืœื—ื”. **/
54 mentions?: CommentUserMention[]
55 /** ื”ื“ื•ืžื™ื™ืŸ ืฉืžืžื ื• ื”ื’ื™ืขื” ื”ืชื’ื•ื‘ื”. **/
56 domain?: string
57 /** ืจืฉื™ืžื” ืื•ืคืฆื™ื•ื ืœื™ืช ืฉืœ ืžื–ื”ื™ ืงื‘ื•ืฆื•ืช ื”ืžื•ื“ืจืฆื™ื” ื”ืžืฉื•ื™ื›ื™ื ืœืชื’ื•ื‘ื” ื–ื•. **/
58 moderationGroupIds?: string[]|null
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.

ืื•ื‘ื™ื™ืงื˜ ื”ืžืื–ื›ืจื™ื ืฉืœ Webhook
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** ื”-id ืฉืœ ื”ืžืฉืชืžืฉ. ืขื‘ื•ืจ ืžืฉืชืžืฉื™ SSO, ื™ื”ื™ื” ืขื ืงื™ื“ื•ืžืช ืžื–ื”ื” ื”-tenant ืฉืœื›ื. **/
4 id: string
5 /** ื”ื˜ืงืกื˜ ื”ืกื•ืคื™ ืฉืœ ืชื’ ื”-@, ื›ื•ืœืœ ืกื™ืžืŸ ื”-@. **/
6 tag: string
7 /** ื”ื˜ืงืกื˜ ื”ืžืงื•ืจื™ ืฉืœ ืชื’ ื”-@, ื›ื•ืœืœ ืกื™ืžืŸ ื”-@. **/
8 rawTag: string
9 /** ืกื•ื’ ื”ืžืฉืชืžืฉ ืฉืกื•ืžืŸ. user = ื—ืฉื‘ื•ืŸ FastComments.com. sso = SSOUser. **/
10 type: 'user'|'sso'
11 /** ืื ื”ืžืฉืชืžืฉ ื‘ื—ืจ ืœื ืœืงื‘ืœ ื”ืชืจืื•ืช, ืขืจืš ื–ื” ืขื“ื™ื™ืŸ ื™ื•ื’ื“ืจ ื›-true. **/
12 sent: boolean
13}
14

ืฉื™ื˜ื•ืช HTTP

ื ื™ืชืŸ ืœื”ื’ื“ื™ืจ ืืช ืฉื™ื˜ืช ื”-HTTP ืขื‘ื•ืจ ื›ืœ ืกื•ื’ ืื™ืจื•ืข webhook ื‘ืœื•ื— ื”ื ื™ื”ื•ืœ:

  • ืื™ืจื•ืข Create: POST ืื• PUT (ื‘ืจื™ืจืช ืžื—ื“ืœ: PUT)
  • ืื™ืจื•ืข Update: POST ืื• PUT (ื‘ืจื™ืจืช ืžื—ื“ืœ: PUT)
  • ืื™ืจื•ืข Delete: DELETE, POST, ืื• PUT (ื‘ืจื™ืจืช ืžื—ื“ืœ: DELETE)

ืžืื—ืจ ืฉื›ืœ ื”ื‘ืงืฉื•ืช ืžื›ื™ืœื•ืช ืžื–ื”ื”, ืคืขื•ืœื•ืช Create ื•-Update ื”ืŸ ืื™ื“ืžืคื•ื˜ื ื˜ื™ื•ืช ื›ื‘ืจื™ืจืช ืžื—ื“ืœ (PUT). ื—ื–ืจื” ืขืœ ืื•ืชื” ื‘ืงืฉืช Create ืื• Update ืœื ืืžื•ืจื” ืœื™ืฆื•ืจ ืื•ื‘ื™ื™ืงื˜ื™ื ื›ืคื•ืœื™ื ื‘ืฆื“ ืฉืœื›ื.

ื›ื•ืชืจื•ืช ื”ื‘ืงืฉื”

ื›ืœ ื‘ืงืฉืช webhook ื›ื•ืœืœืช ืืช ื”ื›ื•ืชืจื•ืช ื”ื‘ืื•ืช:

ื›ื•ืชืจืช ืชื™ืื•ืจ
Content-Type application/json
token ืกื•ื“ ื”-API ืฉืœืš
X-FastComments-Timestamp ื—ื•ืชืžืช ื–ืžืŸ Unix (ื‘ืฉื ื™ื•ืช) ื›ืืฉืจ ื”ื‘ืงืฉื” ื ื—ืชืžื”
X-FastComments-Signature ื—ืชื™ืžืช HMAC-SHA256 (sha256=<hex>)

ืจืื” ืื‘ื˜ื—ื” ื•ืžืคืชื—ื•ืช API ืœืžื™ื“ืข ืขืœ ืื™ืžื•ืช ื—ืชื™ืžืช HMAC.


ืื‘ื˜ื—ื” ื•ืืกื™ืžื•ื ื™ API Internal Link

FastComments webhook requests include multiple authentication mechanisms for security.

Headers Sent

Header Description
token Your API Secret (for backwards compatibility)
X-FastComments-Timestamp Unix timestamp (seconds) when the request was signed
X-FastComments-Signature HMAC-SHA256 signature of the payload

We strongly recommend verifying the HMAC signature to ensure webhook payloads are authentic and haven't been tampered with.

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;
    }

    // ื•ื™ื“ื ืฉื—ื•ืชืžืช ื”ื–ืžืŸ ืขื“ื›ื ื™ืช (ื‘ืชื•ืš 5 ื“ืงื•ืช)
    const now = Math.floor(Date.now() / 1000);
    if (Math.abs(now - parseInt(timestamp, 10)) > 300) {
        return false;  // ืžื ื™ืขืช ื”ืชืงืคืช replay
    }

    // ืืžืช ืืช ื”ื—ืชื™ืžื”
    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

    # ืืžืช ืฉื—ื•ืชืžืช ื”ื–ืžืŸ ืขื“ื›ื ื™ืช
    now = int(time.time())
    if abs(now - int(timestamp)) > 300:
        return False

    # ืืžืช ืืช ื”ื—ืชื™ืžื”
    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;
    }

    // ื•ื™ื“ื ืฉื—ื•ืชืžืช ื”ื–ืžืŸ ืขื“ื›ื ื™ืช (ื‘ืชื•ืš 5 ื“ืงื•ืช)
    $now = time();
    if (abs($now - intval($timestamp)) > 300) {
        return false;
    }

    // ืืžืช ืืช ื”ื—ืชื™ืžื”
    $payload = json_encode($body, JSON_UNESCAPED_SLASHES);
    $message = $timestamp . '.' . $payload;
    $expectedSignature = 'sha256=' . hash_hmac('sha256', $message, $apiSecret);

    return hash_equals($expectedSignature, $signature);
}

Legacy Authentication

The token header containing your API Secret is still sent for backwards compatibility. However, we recommend migrating to HMAC verification for improved security as it protects against replay attacks.


ืœืกื™ื›ื•ื

ื–ื” ืžืกื™ื™ื ืืช ืชื™ืขื•ื“ ื”-Webhooks ืฉืœื ื•.

ืื ื• ืžืงื•ื•ื™ื ืฉืชืžืฆืื• ืืช ืื™ื ื˜ื’ืจืฆื™ื™ืช ื”-Webhook ืฉืœ FastComments ืงืœื” ืœื”ื‘ื ื” ื•ืžื”ื™ืจื” ืœื”ืงืžื”.

ืื ืืชื ืžืจื’ื™ืฉื™ื ืฉื–ื™ื”ื™ืชื ื—ื•ืกืจื™ื ื‘ืชื™ืขื•ื“ ืฉืœื ื•, ื”ื•ื“ื™ืขื• ืœื ื• ืœืžื˜ื”.