FastComments.com

Webhook


FastComments では、コメントがシステムに追加、更新、または削除されるたびに API エンドポイントを呼び出すことができます。

これは HTTP/HTTPS を介した非同期 Webhook によって実現します。


Webhookとは何か Internal Link


Webhookは、2つのシステム間の仕組み、または統合であり、"producer" (FastComments) がイベントを発生させ "consumer" (あなた) がAPIコールでそれを受け取るものです。


サポートされているイベントとリソース Internal Link


FastComments は Comment リソースに対してのみ Webhook をサポートしています。

コメントの作成、削除、更新に対する Webhook をサポートしています。

これらはすべて当社システム内で個別のイベントとみなされ、そのため Webhook イベントには異なるセマンティクス と構造があります。

ダッシュボードまたは API を通じて(「API での Webhook の管理」を参照)、任意の数のエンドポイントが同じイベントを購読できます。 各 Webhook は個別に配信されます。


テスト 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.

ペイロードの検証

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

  1. X-FastComments-Timestamp - Unix タイムスタンプ(秒)
  2. X-FastComments-Signature - HMAC-SHA256 署名

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.

テストツール

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

イベントタイプ

  • Create Event: 新しいコメントが作成されたときにトリガーされます。
  • Update Event: コメントが編集されたときにトリガーされます。
  • Delete Event: コメントが削除されたときにトリガーされます。

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).


データ構造 Internal Link

The only structure sent via webhooks is the WebhookComment object, outlined in TypeScript below.

WebhookComment オブジェクト構造

「Create」イベント構造

「create」イベントのリクエストボディは WebhookComment オブジェクトです。

「Update」イベント構造

「update」イベントのリクエストボディは WebhookComment オブジェクトです。

「Delete」イベント構造

「delete」イベントのリクエストボディは WebhookComment オブジェクトです。

2023年11月14日以降の変更
以前は「delete」イベントのリクエストボディはコメント ID のみが含まれていました。現在は削除時点の完全なコメントが含まれます。

すべてのキーは常にボディに存在します。フィールドに値がない場合、ボディは null(ブール値の場合は false、リストの場合は [])を保持します。そのため、配信の形状はコメントごとに変わりません。

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

コメント内でユーザーがタグ付けされると、その情報は mentions というリストに保存されます。そのリスト内の各オブジェクトは以下の構造を持ちます。

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

HTTP メソッド

管理パネルで各 webhook イベントタイプの HTTP メソッドを設定できます:

  • Create Event: POST または PUT(デフォルト: PUT)
  • Update Event: POST または PUT(デフォルト: PUT)
  • Delete Event: DELETE、POST、または PUT(デフォルト: DELETE)

すべてのリクエストに ID が含まれるため、Create と Update の操作はデフォルトで冪等です(PUT)。同じ Create または Update リクエストを繰り返しても、側で重複したオブジェクトは作成されません。

リクエストヘッダー

各 webhook リクエストには以下のヘッダーが含まれます:

HeaderDescription
Content-Typeapplication/json
tokenあなたの API シークレット
X-FastComments-Timestampリクエストが署名されたときの Unix タイムスタンプ(秒)
X-FastComments-SignatureHMAC-SHA256 署名 (sha256=<hex>)

HMAC 署名の検証に関する情報は、Security & API Tokens を参照してください。

セキュリティとAPIトークン Internal Link

FastComments の webhook リクエストは、セキュリティのために複数の認証メカニズムを含みます。

送信されるヘッダー

ヘッダー説明
tokenAPI シークレット (下位互換性のため)
X-FastComments-Timestampリクエストが署名されたときの Unix タイムスタンプ(秒)
X-FastComments-Signatureペイロードの HMAC-SHA256 署名

HMAC 署名検証(推奨)

Webhook のペイロードが正当で改ざんされていないことを保証するために、HMAC 署名の検証を強く推奨します。

署名形式: sha256=<hex-encoded-signature>

署名の計算方法:

  1. 連結: timestamp + "." + JSON_payload_body
  2. API シークレットをキーとして HMAC-SHA256 を計算
  3. 結果を十六進エンコード

検証例 (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;  // リプレイ攻撃防止
    }

    // 署名を確認
    const payload = JSON.stringify(req.body);
    const expectedSignature = crypto
        .createHmac('sha256', apiSecret)
        .update(`${timestamp}.${payload}`)
        .digest('hex');

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

検証例 (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}"

検証例 (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);
}

レガシー認証

token ヘッダーは下位互換性のために引き続き送信されますが、リプレイ攻撃から保護するために、セキュリティ向上の観点から HMAC 検証への移行を推奨します。

APIを使用したWebhookの管理 Internal Link


Webhooks は REST API を通じても管理できます。これは、Zapier のような統合がダッシュボードに触れずにコメントイベントを購読する方法で、REST Hooks パターン(購読、イベント受信、購読解除)に従います。

API サブスクリプションは、ダッシュボードで設定された Webhooks と共存します。コメントイベントは、ドメインが一致するすべての Webhook に対して個別に配信され、Webhook の作成方法に関係なく配信されます。

Authentication

すべてのリクエストは、x-api-key ヘッダー(または API_KEY クエリパラメータ)に API キーを、tenantId クエリパラメータにテナント ID を含める必要があります。これらはダッシュボードの API Secret ページに表示されています。

Subscribe

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"
}
FieldRequiredDescription
urlYes絶対的な http または https の URL。
eventYescomment-createdcomment-updated、または comment-deleted
domainNoアカウント設定からのドメイン。デフォルトは * で、すべてのドメインのイベントを受信します。
methodNoPOST(デフォルト)、PUT、または DELETE。

レスポンスにはサブスクリプションが含まれます:

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

同じ URL を同じイベントとドメインで再度購読すると、重複を作成せずに既存のサブスクリプションが返されるため、クライアントは安全に再試行できます。各テナントは最大 50 件の API サブスクリプションを持つことができます。

List

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

テナントのすべての Webhook を返します。ダッシュボードで管理されているもの("source": "dashboard")も含まれます。eventdomain、または source でフィルタリングできます。

Unsubscribe

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

サブスクリプションを削除すると、キューに残っているイベントも破棄されます。この方法で削除できるのは API 経由で作成されたサブスクリプションのみです。ダッシュボードの Webhook や、アカウントに存在しない ID は 404 とコード not-found を返します。ダッシュボードの Webhook は Webhooks ページで編集します。

Payloads and signing

配信はダッシュボードの Webhook と同じペイロードを使用し(Data Structures 参照)、同じ HMAC 方式で署名されます(Security & API Tokens 参照)。API サブスクリプションはレガシーの token ヘッダーを受け取らないため、代わりに X-FastComments-Signature ヘッダーを検証してください。

Sample payloads

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

配信が保持する形そのままで、アカウントの最新コメントを返すため、統合は最初のイベントが到着する前に実際のサンプルデータを表示できます。event はオプションで、すべてのイベントが同じコメントオブジェクトを配信するため検証のみ行われます。limit のデフォルトは 3 で、1 から 10 の範囲で指定可能です。2 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
        }
    ]
}

Responding with 410 Gone

API サブスクリプションのエンドポイントが HTTP 410 Gone を返した場合、FastComments はそれを購読解除とみなし、サブスクリプションとキューに残っているイベントが削除され、以降の配信は行われません。ダッシュボードで設定された Webhook は自動的に削除されることはなく、410 は通常の失敗として扱われます。その他の失敗ステータスは再試行され、最終的に Webhook が無効化されます(How it Works & Handling Retries 参照)。

Dashboard

API サブスクリプションは Webhooks リストに API ソースとして表示され、管理者はそれらを編集、無効化、再有効化、または削除できます。



結論

これで Webhooks ドキュメントは終了です。

FastComments の Webhook 統合がわかりやすく、迅速に設定できることを願っています。

ドキュメントに不備があると感じた場合は、下記からお知らせください。