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

新規および編集用のWebhookページには、Send Test Payload ボタンがあり、保存済みかどうかに関わらず、フォームに入力されているURLにリクエストを送信します。CreateイベントとUpdateイベントはダミーのWebhookCommentオブジェクトを送信し、DeleteのテストではIDのみを含むダミーのリクエストボディを送信します。

ペイロードの検証

Webhook統合をテストする際、受信リクエストに以下のヘッダーが含まれていることを確認してください:

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

署名スキームが導入される前に作成されたWebhookも、APIシークレットを含む token ヘッダーを受け取ります。新しいWebhookでは受け取りません。

ペイロードが本物であることを確認するために、HMAC署名検証を使用してください。

テストツール

開発中に受信したWebhookペイロードを検査するには、webhook.site や ngrok などのツールを使用できます。

イベントタイプ

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

各Webhookは1つのイベントと1つのHTTPメソッド(POST、PUT、またはDELETE)に紐付けられています。各イベントには、リクエストボディにコメントの完全なデータが含まれます(ペイロード形式については データ構造 を参照してください)。

データ構造 Internal Link

Webhook経由で送信される構造はWebhookCommentオブジェクトのみで、以下にTypeScriptで示します。

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 /** コメントのID。 **/
4 id: string
5 /** コメントスレッドを識別するIDまたはURL。正規化されています。 **/
6 urlId: string
7 /** コメントが投稿された場所を指すURL。 **/
8 url: string | null
9 /** コメントを投稿したユーザーのID。SSOの場合はテナントIDが接頭辞として付きます。 **/
10 userId: string | null
11 /** コメントを投稿したユーザーのメールアドレス。 **/
12 commenterEmail: string | null
13 /** コメントウィジェットに表示されるユーザーの名前。SSOの場合はdisplayNameになることがあります。 **/
14 commenterName: string
15 /** 生のコメントテキスト。 **/
16 comment: string
17 /** パース後のコメントテキスト。 **/
18 commentHTML: string
19 /** コメントの外部ID。 **/
20 externalId: string | null
21 /** 親コメントのID。 **/
22 parentId: string | null
23 /** コメントが投稿されたUTC日時。 **/
24 date: UTC_ISO_DateString
25 /** 投票の合計カルマ(賛成 - 反対)。 **/
26 votes: number
27 votesUp: number
28 votesDown: number
29 /** ユーザーがコメント時にログインしていたか、コメントを認証したか、コメント投稿時にセッションを認証した場合にtrue。 **/
30 verified: boolean
31 /** コメントが認証されたUTC日時。 **/
32 verifiedDate: UTC_ISO_DateString | null
33 /** モデレーターがコメントをレビュー済みとしてマークしたかどうか。 **/
34 reviewed: boolean
35 /** アバターの場所、またはbase64エンコーディング。base64になるのはSSOでその値が渡された場合のみです。 **/
36 avatarSrc: string | null
37 /** コメントが手動または自動でスパムとしてマークされたかどうか。 **/
38 isSpam: boolean
39 /** コメントが自動的にスパムとしてマークされたかどうか。 **/
40 aiDeterminedSpam: boolean
41 /** コメントに画像が含まれているかどうか。 **/
42 hasImages: boolean
43 /** "Most Relevant"ソート方向でのコメントのページ番号。 **/
44 pageNumber: number | null
45 /** "Oldest First"ソート方向でのコメントのページ番号。 **/
46 pageNumberOF: number | null
47 /** "Newest First"ソート方向でのコメントのページ番号。 **/
48 pageNumberNF: number | null
49 /** コメントが自動または手動で承認されたかどうか。 **/
50 approved: boolean
51 /** コメント作成時のユーザーのロケールコード(形式: en_us)。 **/
52 locale: string | null
53 /** コメント内で正常にパースされた@メンション。ない場合は空。 **/
54 mentions: CommentUserMention[]
55 /** コメントの送信元ドメイン。 **/
56 domain: string | null
57 /** このコメントに関連付けられたモデレーショングループのID。ない場合は空。 **/
58 moderationGroupIds: string[]
59 /** このコメントでコメント投稿者の名前の横に表示されるバッジ(イベント作成時点のもの)。ない場合は空。 **/
60 badges: CommentUserBadgeInfo[]
61}
62

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

Webhookメンションオブジェクト
Copy CopyRun External Link
1
2interface CommentUserMention {
3 /** ユーザーID。SSOユーザーの場合はテナントIDが接頭辞として付きます。 **/
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

badges リストの各エントリは以下の構造を持ちます。バッジの種類についてはバッジガイドを参照してください。

Webhookバッジオブジェクト
Copy CopyRun External Link
1
2interface CommentUserBadgeInfo {
3 /** このユーザーに付与されたバッジのID。 **/
4 id: string
5 /** バッジの種類。 **/
6 type: number
7 /** バッジの説明。バッジにホバーすると表示されます。 **/
8 description: string
9 /** バッジに表示されるテキスト。 **/
10 displayLabel?: string | null
11 /** バッジに表示される画像(使用する場合)。 **/
12 displaySrc?: string | null
13 backgroundColor?: string | null
14 borderColor?: string | null
15 textColor?: string | null
16 /** バッジに適用される追加のCSSクラス。 **/
17 cssClass?: string | null
18}
19

HTTPメソッド

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

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

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

リクエストヘッダー

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

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

HMAC署名の検証方法については、セキュリティとAPIトークンを参照してください。

セキュリティと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

WebhookはREST APIからも管理できます。Zapierなどの統合がダッシュボードを操作せずにコメントイベントを購読する仕組みで、REST Hooksパターン(購読、イベント受信、購読解除)に従います。

APIによる購読は、ダッシュボードで設定されたWebhookと並んで管理されます。コメントイベントは、Webhookの作成方法にかかわらず、そのドメインに一致するすべてのWebhookに対して、それぞれ個別の配信として送られます。

認証

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

購読

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"
}
フィールド必須説明
urlはい絶対URL(httpまたはhttps)。
eventはいcomment-created、comment-updated、またはcomment-deleted。
domainいいえアカウント設定に含まれるドメイン。デフォルトは*で、すべてのドメインのイベントを受け取ります。
methodいいえPOST(デフォルト)、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購読が可能です。

一覧取得

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

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

購読解除

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

購読を削除すると、その購読のためにキューに残っているイベントも破棄されます。この方法で削除できるのはAPI経由で作成された購読のみです。ダッシュボードのWebhookや、アカウントに存在しないIDを指定した場合は、コードnot-foundとともに404が返されます。ダッシュボードのWebhookはWebhooksページで編集してください。

ペイロードと署名

配信にはダッシュボードのWebhookと同じペイロードが使用され(データ構造を参照)、同じHMACスキームで署名されます(セキュリティとAPIトークンを参照)。API購読では従来のtokenヘッダーは送られないため、代わりにX-FastComments-Signatureヘッダーを検証してください。

サンプルペイロード

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

アカウントの最新のコメントを、配信時に含まれるのとまったく同じ形式で返します。これにより、最初のイベントが届く前に統合側で実際のサンプルデータを表示できます。eventは省略可能で、検証のみ行われます。すべてのイベントで同じコメントオブジェクトが配信されるためです。limitのデフォルトは3で、1から10まで指定できます。APIクレジットを2消費します。

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

410 Goneでの応答

API購読のエンドポイントがHTTP 410 Goneで応答した場合、FastCommentsはそれを購読解除として扱います。購読はキューに残っているイベントとともに削除され、それ以降の配信は試行されません。ダッシュボードで設定されたWebhookが自動的に削除されることはなく、これらにとって410は通常の失敗です。その他の失敗ステータスはリトライされ、最終的にWebhookが無効化されます。詳細は「仕組みとリトライの処理」を参照してください。

ダッシュボード

API購読は、ソースがAPIとしてWebhooks一覧に表示され、管理者はそこで編集、無効化、再有効化、削除を行えます。

結論

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

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

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