本文へスキップ
LinkProfit

Webhook

署名付きのイベント配信。利用できるイベント、JavaScriptとPythonでのX-LinkProfit-Signatureの検証、再試行、失敗状態について説明します。

2026年8月13日更新

Webhookは、ポーリングなしで、JSONのPOSTリクエストとしてイベントをあなたのエンドポイントへ 届けます。エンドポイントの登録は、ダッシュボード(設定 → Webhook)か POST /v1/webhooksで行います。署名用のシークレットは作成時に一度だけ表示されます。

イベント

| イベント | 発火する条件 | 対象 | |---|---|---| | link.created / link.updated / link.deleted | リンクが変更されたとき。ダッシュボード、API、一括インポートのいずれでも | ワークスペースとパートナーのエンドポイント | | domain.pending_ssl | DNSが確認され、証明書を発行中 | ワークスペースとパートナー | | domain.activated | ドメインがトラフィックを配信し始めた | ワークスペースとパートナー | | domain.error | 構成に失敗した。詳細は本文に含まれる | ワークスペースとパートナー | | click.threshold | 月あたりの計測クリック上限の80% / 100%に達した | ワークスペースとパートナー | | conversion.created | コンバージョンが記録された。クリックに紐づいた注文、登録、独自の目標 | ワークスペースとパートナー | | workspace.subscribed | クライアントがサブスクリプションの最初の請求を支払った | パートナーのみ | | workspace.past_due | クライアントの支払いが失敗した | パートナーのみ | | workspace.suspended | クライアントが停止された(督促または手動) | パートナーのみ | | payment.succeeded | クライアントの支払いが清算された。手数料の内訳付き | パートナーのみ | | payment.refunded | 支払いが返金された | パートナーのみ |

配信の中身

POST /your/endpoint HTTP/1.1
Content-Type: application/json
X-LinkProfit-Event: link.created
X-LinkProfit-Delivery: dlv_8Yq2…
X-LinkProfit-Signature: t=1755081600,v1=5f8a2c…
{
  "id": "dlv_8Yq2…",
  "type": "link.created",
  "created_at": "2026-08-13T10:00:00.000Z",
  "data": { "id": "lnk_…", "domain": "go.your-brand.com", "slug": "x7f2q1z", "url": "https://example.com" }
}

10秒以内に2xxのいずれかで応答してください。それ以外は——タイムアウトを含めて——失敗として 数えられ、再試行が予約されます。配信は順序が前後することがあり、まれに二重に届くこともある ので、idで重複を除去してください。

署名の検証

署名は、エンドポイントのシークレットを鍵として`${t}.${rawBody}`に対して計算した HMAC-SHA256を、16進数で表したものです。JSONを解析する前の生のリクエスト本文に対して 必ず検証し、5分より古いタイムスタンプは拒否してください。

import { createHmac, timingSafeEqual } from "node:crypto";

function verifySignature(secret, rawBody, header, toleranceSeconds = 300) {
  const parts = Object.fromEntries(
    header.split(",").map((piece) => piece.split("=", 2)),
  );
  if (!parts.t || !parts.v1) return false;
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > toleranceSeconds) return false;

  const expected = createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1);
  return a.length === b.length && timingSafeEqual(a, b);
}
import hashlib
import hmac
import time


def verify_signature(secret: str, raw_body: bytes, header: str, tolerance: int = 300) -> bool:
    parts = dict(piece.split("=", 1) for piece in header.split(",") if "=" in piece)
    timestamp, signature = parts.get("t"), parts.get("v1")
    if not timestamp or not signature:
        return False
    if abs(time.time() - int(timestamp)) > tolerance:
        return False

    expected = hmac.new(
        secret.encode(), f"{timestamp}.".encode() + raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

再試行と失敗状態

最初の試行はすぐに行われます。失敗した場合は、決まったスケジュールで再試行します。 1分 → 5分 → 30分 → 2時間 → 12時間です。5回目の再試行の後、そのエンドポイントは failingと記録され、新しいイベントの受信を止め、所有者にメールが届きます。

エンドポイントを直したうえで、ダッシュボードか、PATCH /v1/webhooks/{id}{"status": "active"}を指定して再度有効にしてください。取りこぼしたイベントは自動では 再送されません。配信ログ(GET /v1/webhooks/{id}/deliveriesまたはダッシュボード)に すべてのペイロードが残っているので、突き合わせができます。

配信が1回成功すると、失敗の連続はリセットされます。

テスト

テスト送信ボタン(またはPOST /v1/webhooks/{id}/test)は、"data": { "test": true } を含む本物の署名付きイベントを、本番のイベントと同じ経路で配信します。配信ログには、あなたの エンドポイントの応答が表示されます。