はじめに

2つのサービス間で連携を設定したことがある方——たとえば、あるシステムのデータをCRMやGoogleスプレッドシートに自動的に取り込むためなど——であれば、「Webhook」という言葉を目にしたことがあるはずです。これは、Slack通知からオンラインショップの注文同期まで、現代の自動化の多くを目立たないところで支えている技術の一つです。 この記事では、Webhookとは何かをわかりやすく解説し、通常のAPIリクエストとの違い、そしてLix.liのWebhookを使って短縮リンクのクリック通知を実際に設定する方法を紹介します。

Webhookとは何か、簡単に言うと

Webhookとは、特定のイベントが発生した瞬間に、あるサービスから別のサービスへ自動的にデータを送信する仕組みです。あなたのシステムが「何か新しいものはありますか?」と繰り返し尋ねる代わりに、イベントが発生したその瞬間に、サービス側が自らあなたのサーバーへデータを送ってくれます。 この違いを理解する最も簡単な方法は、郵便に例えることです。

  • 通常のAPIリクエストは、手紙が届いているかどうかを確認するために、自分で何度も郵便受けまで見に行くようなものです。
  • Webhookは、配達サービスに登録するようなものです。手紙が準備できた瞬間、自動的に自宅まで届けられます。 技術的には、Webhookとは、注文の支払い完了、タスクのステータス変更、あるいはLix.liの場合であれば短縮リンクのクリックといった該当のイベントが発生したときに、あるサーバーが別のサーバー上のあらかじめ設定されたURLへ送信する、ごく通常のHTTPリクエスト(多くの場合POST)にすぎません。

Webhookがなぜ必要なのか

Webhookは、ある特定の問題を解決します。それは、外部サービスを常に「ポーリング(polling)」することなく、最新のデータを取得する方法です。 Webhookがなければ、新しいイベントを知るためには、1分おき、5分おきといった間隔でAPIに定期的にリクエストを送り、毎回何か新しいものがないか確認しなければなりません。これは両方のサーバーに無駄な負荷をかけるだけでなく、実際にイベントが発生してから、それを知るまでの間に必ず遅延が生じてしまいます。 Webhookはこのロジックを逆転させます。何かが起きたとき、サービス側からあなたに通知してくれるのです。これは特に次のような場面で役立ちます。

  • 業務プロセスの自動化——たとえば、新しいリードを自動的にCRMへ記録する場合。
  • アクセス解析との連携——クリックデータを自社の集計システムに送る場合。
  • ボットや通知——Telegramボットやチームの Slack チャンネルにイベントを送信する場合。
  • データの同期——Googleスプレッドシート、ダッシュボード、社内システムをリアルタイムで更新する場合。

Webhookの仕組み:Lix.liを例に

Lix.liでは、Webhookを使うことで、短縮リンクのクリックに関する情報を、サービスのAPIを常に問い合わせることなく、自動的に自分のサーバーへ直接受け取ることができます。これらのイベントをCRM、アクセス解析システム、Googleスプレッドシート、Telegramボット、あるいはその他の自動化システムに取り込みたい場合に便利です。

どのように構築されているか

  • クリックは1件ずつ送信されるのではなく、数分ごとにまとめてバッチとして配信されます。これにより、あなたのサーバーとLix.liのサーバー、双方の負荷が軽減されます。
  • 配信はほぼリアルタイムで行われますが、即座ではありません。Webhookは、1回1回のクリックへの即時反応ではなく、数分程度の遅延が許容されるシナリオ向けに設計されています。
  • 配信保証は**「少なくとも1回(at least once)」**という原則に基づいています。送信中にネットワーク障害が発生した場合、バッチが再度届くことがあります。そのため、すべてのイベントには一意のevent_idが付与されており、これを使ってご自身の側で重複を排除する必要があります。

ダッシュボードでのWebhook設定

Webhook機能はPremiumプランで利用できます。設定はわずか数ステップです。

  1. ダッシュボードで**「Webhook」セクションを開き、「追加」**をクリックします。
  2. 以下を指定します。
    • 受信先URL——リクエストを受け取るあなたのサーバーのアドレス(例:https://api.yoursite.com/webhooks/lix)
    • 対象範囲——すべてのリンク、特定のリンクグループ、または1つのリンクのみを対象にイベントを送信するか
    • 訪問者のIPアドレスをイベントデータに含めるかどうか(個人データにあたるため、デフォルトでは無効)
  3. Webhookを作成した直後、一度だけ表示されるシークレットキーが表示されます。これは受信リクエストの正当性を検証するために使用するため、必ず保存してください。
  4. **「テスト」**をクリックすると、サービスがテストイベントを送信し、あなたのサーバーが正しく応答したかどうかが表示されます。 Lix.liダッシュボードでのWebhook作成プロセスと、設定済みWebhookの一覧

あなたのサーバーに届く内容

すべてのリクエストはPOSTメソッドで送信され、本文の形式はapplication/jsonです。データ本体に加えて、リクエストにはいくつかのサービス用ヘッダーが含まれます。

ヘッダー 用途
X-Lix-Signature sha256=形式の本文の署名——正当性の検証に使用します。
X-Lix-Timestamp リクエストが送信された時刻(Unixタイムスタンプ)。
X-Lix-Delivery 一意の配信識別子。
User-Agent Lix-Webhooks/1.0という値が設定されます。
リクエスト本文には、イベントのバッチ自体が含まれています。
{
  "delivery_id": "0f3b9c2e-6a1d-4e88-9d5a-2f8c1b7a4e10",
  "event_type": "redirects.batch",
  "sent_at": "2026-06-01T12:05:00Z",
  "window": {
    "from": "2026-06-01T12:00:00Z",
    "to":   "2026-06-01T12:03:30Z"
  },
  "count": 2,
  "truncated": false,
  "events": [
    {
      "event_id":   "2ef7bde608ce5404e97d5f042f95f89f1c232871",
      "link_id":    12345,
      "datetime":   "2026-06-01T12:01:00Z",
      "country":    "US",
      "city":       "Boston",
      "browser":    "Chrome",
      "os":         "Windows",
      "device":     "Desktop",
      "ref_domain": "google.com",
      "group_id":   null,
      "is_bot":     false
    }
  ]
}

バッチ内の各イベントには、リンクID、UTCでのクリック時刻、国、都市、ブラウザ、OS、デバイスの種類、クリック元のドメイン、グループID(設定されている場合)、そしてボットかどうかを示すフラグが含まれます。訪問者のIPアドレスは、Webhookの設定時にそのオプションを明示的に有効にした場合のみ、データに含まれます。 イベントが非常に多く蓄積された場合、そのバッチにはtruncated: trueというフラグが付くことがあります。これは、データの一部が次回の配信で届くことを意味し、データが失われることはありません。

リクエストの正当性を検証する方法

あなたのサーバーのURLは、技術的にはどこからでもリクエストを受け取れてしまうため、そのリクエストが本当にLix.liから送られたものであり、偽装されたものではないことを確認することが重要です。これがX-Lix-Signatureヘッダーに含まれる署名の役割です。 検証の原理は次のとおりです。リクエストの「生の(未加工の)」本文を取得し、あなたのシークレットキーを使ってHMAC-SHA256を計算し、その結果をヘッダーに含まれる値と比較します。タイミング攻撃を防ぐため、この比較は安全な方法(一定時間比較)で行う必要があります。 PHPの例:

$raw    = file_get_contents('php://input');
$secret = 'your_secret';
$expected = 'sha256=' . hash_hmac('sha256', $raw, $secret);
if (!hash_equals($expected, $_SERVER['HTTP_X_LIX_SIGNATURE'] ?? '')) {
    http_response_code(401);
    exit;
}

Node.js(Express)の例:

const crypto = require('crypto');
// 生の本文を取得することが重要です: app.use(express.raw({ type: 'application/json' }))
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(req.body).digest('hex');
const got = req.header('X-Lix-Signature') || '';
if (expected.length !== got.length ||
    !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(got))) {
  return res.sendStatus(401);
}

Python(Flask)の例:

import hmac, hashlib
raw = request.get_data()  # 生のバイト列
expected = 'sha256=' + hmac.new(secret.encode(), raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, request.headers.get('X-Lix-Signature', '')):
    return '', 401

受信サーバーに求められる要件

Webhookが安定して機能するためには、あなたのサーバーがいくつかのルールを満たす必要があります。

  1. 2xxコードで応答すること。 それ以外のコードやタイムアウトはすべて失敗と見なされ、配信が再試行されます。
  2. 素早く応答すること。 応答できるのはわずか数秒です。リクエストハンドラー内でデータを同期的に処理してはいけません。正しいアプローチは、受け取ったデータを素早く保存し(キューやデータベースなどに)、2xxを返した上で、重い処理は後から別途行うことです。
  3. event_idで重複を排除すること。 再送信の仕組みにより、同じイベントが2回届くことがあります。
  4. 冪等性を保つこと。 同じバッチを再処理しても、あなたのデータが壊れないようにする必要があります。
  5. すべての受信リクエストで署名を必ず検証すること
  6. HTTPSを使用すること。 ローカルアドレスや内部アドレスはWebhookの受信には対応していません。

エラーが発生した場合の動作

あなたのサーバーが2xxコードで応答しない場合、Lix.liは試行の間隔を徐々に広げながら配信を再試行します。受信先が長期間まったく応答しない場合(連続して多くの失敗が発生した場合)、意味のない送信を避けるため、Webhookは自動的に一時停止されます。これはダッシュボードの配信ログに表示され、あなたの側で問題を修正した後、Webhookを再度有効にすることができます。

ダッシュボードでのWebhook管理

設定済みの各Webhookには、以下の操作が利用できます。

  • テスト——テストイベントを1件送信し、結果(成功、またはサーバーからの具体的な応答コード)を即座に確認します。
  • 一時停止 / 再開——イベントの配信を一時的に停止または再開します。
  • シークレットキーの変更——古いキーの代わりに新しいキーを生成します。古いキーは即座に無効になるため、あなたの側でも忘れずにすぐ更新してください。
  • 削除——Webhookを完全に削除します。
  • 配信ログ——直近の配信履歴を、ステータス、HTTP応答コード、バッチ内のイベント数、送信時刻とともに確認できます。

よくある質問

イベントはどれくらい早く届きますか? バッチ単位で、おおよそ数分ごとに届き、処理に多少の遅延があります。これは即時のプッシュ配信ではなく、ほぼリアルタイムのシナリオです。 同じイベントが2回届くことはありますか? はい、ネットワーク障害後に再配信が行われた場合にあり得ます。だからこそ、あなたの側でevent_idフィールドを使ってイベントを重複排除することが重要です。 イベントの厳密な順序は保証されますか? 1つのバッチ内ではイベントは時系列順に並びますが、異なるバッチ間での厳密な順序は保証されません。並び替えには各イベントのdatetimeフィールドを利用してください。 トラフィックが非常に多い場合はどうなりますか? 1つの区間に非常に多くのイベントが蓄積された場合、そのバッチにはtruncated: trueというフラグが付き、残りのデータは次回の配信で届きます。この過程でデータが失われることはありません。 HTTPSは必須ですか? はい、受信先のアドレスはhttps://で始まる必要があります。ローカルアドレスや内部アドレスは受け付けられません。 1つのリンクまたはリンクグループのイベントだけを受け取ることはできますか? はい、Webhookを作成する際に、アカウント内のすべてのリンクの代わりに「1つのリンク」または「グループ」という対象範囲を選択できます。

PHPによる完全なハンドラーの例

以下は、署名を検証し、素早く応答し、重複防止を備えてイベントを処理する、最小限ながら実際に動作するハンドラーの例です。