設定すること
link.created、link.updated、link.deleted、member.invited、domain.verifiedなどのElidoイベントを受け取るエンドポイント。X-Elido-SignatureヘッダーをHMAC-SHA256で検証し、各配信がElidoから送られたことをサーバーで確認します。- イベントごとの3回の配信試行、手動のRetryボタンを備えた配信ログ、7日間の重複期間を設けたシークレットローテーション。
Webhookはイベントの発生時にElidoからサーバーへプッシュするため、APIをポーリングする必要がありません。一般的な用途には、リンクのメタデータをCRMに同期すること、リンクの内部インデックスを最新に保つこと、リンクが公開されたときにジョブを開始することなどがあります。
Webhookはリンク、ドメイン、メンバー、ワークスペースの変更を扱います。個々のクリックは含まれません。クリックデータにはクリックエクスポートまたはMixpanelなどのイベントインテグレーションを使用してください。クリックごとのWebhookイベントは提供予定です。
Webhookエンドポイントを追加する
- Dashboard → Webhooks に移動します。
- POST先のURLと、任意の説明を入力します。
- エンドポイントの種類として、Events(イベント種別を選択)または SIEM (firehose)(ワークスペース内のすべてのイベント)を選択します。
- Eventsエンドポイントの場合は、必要なイベント種別にチェックを入れます。
link.*イベントにはデフォルトでチェックが入っています。 - Add endpoint をクリックします。署名シークレットはフォームのすぐ下に一度だけ表示されます。Elidoでは再表示できないため、ここでコピーしてください。
URLはHTTPSである必要があります。プレーンなhttp:// URLはエンドポイントを保存すると拒否されますが、localhostと127.0.0.1はローカル開発用として引き続き許可されます。
エンドポイントは一覧から一時停止・再開できます。一時停止したエンドポイントには新しい配信は送られず、キューに残っているものは再開するまで待機します。
Webhookを管理できるユーザー
ワークスペース内の全員がエンドポイントと配信ログを閲覧できます。作成、編集、一時停止、削除、シークレットのローテーション、配信の再試行にはworkspace.edit権限が必要です。この権限は組み込みの Admin と Owner ロールが持っています。APIキーも、そのロールにこの権限があれば、APIの同じWebhookエンドポイントを呼び出せます。
エンドポイントの種類
- Events(
event):購読したイベント種別のみを受け取ります。署名付きです。 - SIEM(
siem):audit.eventを含め、ワークスペースで公開されたすべてのイベントを受け取ります。署名付きです。ログパイプラインやセキュリティツール向けです。 - Discord、Telegram、Sentry:同じ配信システムを再利用するチャット・アラートチャンネルですが、ベンダー独自のメッセージ形式で送信します。ベンダーのURLまたはトークンで認証するため、署名ヘッダーはありません。Discord、Telegram、Sentryを参照してください。
以下の署名とシークレットに関する説明は、EventsとSIEMエンドポイントにのみ適用されます。
イベントカタログ
名前は<resource>.<action>の形式です。ダッシュボードのフォームには最も一般的なものが表示されます。APIでは、この一覧にある任意の種別をEventsエンドポイントに購読させることができ、SIEMエンドポイントはすべてを受け取ります。
ダッシュボードの選択欄で利用できるもの:
link.created:一括作成されたリンクを含む、短縮リンクが作成された。link.updated:リンク(遷移先、タイトル、タグ、有効期限など)が変更された、復元された、ステータスが変更された、またはモデレーションによって安全性ステータスが変更された。link.deleted:リンクがソフトデリートされた。workspace.updated:ワークスペースの設定が変更された。workspace.created:ワークスペースの作成時に公開される。作成前のワークスペースにはエンドポイントを存在させられないため、通常は受信しません。member.invited:直接追加、招待の承諾、SCIMプロビジョニングによって、誰かがワークスペースに参加した。member.removed:手動またはSCIMによってメンバーが削除された。audit.event:監査ログの各エントリにつき1件のイベント。
APIまたはSIEMエンドポイントで利用できるもの:
link.expired:リンクの有効期限が過ぎた。link.cap_reached:リンクがクリック上限に達した。dataにはcapとclicksが含まれます。link.broken:Elidoのリンクヘルスチェックで、遷移先に障害があることが検出された。dataにはstatus_code、error_message、first_seen_atが含まれます。link.encrypted_createdとlink.encryption_rotated:暗号化されたリンク用です。domain.verified:カスタムドメインがDNSチェックに合格した。domain.ssl_failed:追加から24時間経ってもカスタムドメインのDNSレコードが見つからなかった。名前に反して、これは証明書の更新ではなくDNS検証に関するイベントです。member.role_changed:メンバーのロールが変更された。dataにはold_role、new_role、changed_by_user_idが含まれます。invitation.createdとinvitation.accepted。- 請求イベント:
subscription.*(例:subscription.activated、subscription.renewed、subscription.canceled)およびusage.threshold_80、usage.threshold_100。
提供予定で、現在は送信されないもの:link.clicked.aggregated(ウィンドウごとのリンク別クリック数)、クリックごとのイベント、conversion.recorded、domain.tls_renewed、qr.generated、独立したabuse.flaggedイベント。現在、リンクに対するモデレーションの判定は、新しいabuse_statusを含むlink.updatedとして届きます。
ペイロードの形式
署名付きの各配信は、Content-Type: application/json、User-Agent: Elido-Webhooks/1.0を含むPOSTで、次のエンベロープを持ちます。
{
"type": "link.created",
"workspace_id": 4123,
"timestamp": "2026-09-22T09:42:11Z",
"data": {
"id": 891234,
"workspace_id": 4123,
"domain_id": 12,
"slug": "spring-2026",
"destination_url": "https://acme.com/spring-sale",
"title": "Spring sale",
"tags": ["email"],
"status": "active",
"expires_at": null,
"created_by_user_id": 5821,
"created_at": "2026-09-22T09:42:11Z"
}
}
typeはイベント種別です。X-Webhook-Eventヘッダーにも含まれます。timestampはイベントが発生した時刻で、UTCです。dataはイベントによって異なります。link.*イベントでは完全なリンクオブジェクトです(上の例は一部を省略しています)。その他のイベントでは、カタログに記載したIDとフィールドを含む小さなオブジェクトです。- シークレットがElidoの外に出ることはありません。リンクのパスワードハッシュや招待トークンなどのフィールドは、配信前に
dataから削除されます。
現時点では本文にイベントidフィールドはありません。イベントごとのIDは提供予定です。冪等性には、後述するX-Webhook-Deliveryヘッダーを使用してください。
ヘッダーと署名
EventsまたはSIEMエンドポイントへの各配信には、次のヘッダーが含まれます。
| Header | Value |
|---|---|
X-Elido-Signature | v1=に続く16進数のHMAC-SHA256署名 |
X-Webhook-Signature | X-Elido-Signatureと同じ値。古いインテグレーション向けに維持されています |
X-Webhook-Timestamp | このリクエストに署名した時点のUnix秒 |
X-Webhook-Event | イベント種別。例:link.created |
X-Webhook-Delivery | 配信ID。同じ配信のリトライでは同じ値が維持されます |
X-Elido-Signature-Previous | シークレットローテーション中のみ。古いキーで作成した同じ署名 |
署名は次のように計算します。
v1=hex( HMAC-SHA256( secret, "{X-Webhook-Timestamp}.{raw request body}" ) )
キーにはwhsec_プレフィックスを含むシークレット文字列全体を、そのまま使用します。受信した生のバイト列に署名し、再シリアライズしたJSONオブジェクトには署名しないでください。比較には定時間比較を使用します。Node(Express)の短い例を示します。
import express from "express";
import { createHmac, timingSafeEqual } from "node:crypto";
const SECRET = process.env.ELIDO_WEBHOOK_SECRET; // full whsec_... value
function same(a, b = "") {
const x = Buffer.from(a);
const y = Buffer.from(b);
return x.length === y.length && timingSafeEqual(x, y);
}
const app = express();
app.post(
"/webhooks/elido",
express.raw({ type: "application/json" }),
(req, res) => {
const ts = req.get("X-Webhook-Timestamp") ?? "";
if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) {
return res.status(400).send("stale timestamp");
}
const expected =
"v1=" +
createHmac("sha256", SECRET)
.update(`${ts}.`)
.update(req.body)
.digest("hex");
const ok = [
req.get("X-Elido-Signature"),
req.get("X-Elido-Signature-Previous"),
].some((sig) => same(expected, sig));
if (!ok) return res.status(401).send("bad signature");
const event = JSON.parse(req.body.toString("utf8"));
// queue event for processing, then answer quickly
res.sendStatus(200);
},
);
PythonとGoの例、テストベクター、よくある間違いについては、Webhook署名の検証方法を参照してください。
署名シークレットをローテーションする
Dashboard → Webhooks でエンドポイントを開き、Rotate secret をクリックします。新しいwhsec_シークレットが一度だけ表示され、それ以降、Elidoはそのシークレットで署名します。
古いキーは7日間の猶予期間中も有効です。この期間、各配信には2つの署名が含まれます。X-Elido-Signatureは新しいキー、X-Elido-Signature-Previousは古いキーで作成されるため、まだ切り替えていない受信側でも検証を続けられます。新しいシークレットを展開する間は、どちらのヘッダーも受け入れてください。
2026年9月22日より前に作成されたエンドポイントは例外です。最初のローテーションではX-Elido-Signature-Previousが生成されないため、元のシークレットは直ちに使えなくなります。その最初のローテーションの直後に、新しいシークレットをデプロイしてください。同日以降に作成されたエンドポイントでは、最初のローテーションから2つの署名が付与されます。2回目以降のローテーションでは、7日間の重複期間が適用されます。
リトライ
2xx以外のレスポンス、ネットワークエラー、または10秒以内にレスポンスがない場合、配信は失敗します。各配信の試行回数は合計3回です。
- 最初の試行は、イベントの数秒後に行われます。
- 最初の失敗から約5分後にリトライします。
- 2回目の失敗から約15分後に最後のリトライを行います。
3回目の試行も失敗すると、配信は failed とマークされ、自動では再試行されません。エンドポイントはアクティブなままで、新しいイベントは引き続き配信されます。エンドポイントがリクエストを処理したものの10秒のタイムアウト後に応答した場合など、同じ配信が複数回届くことがあります。そのため、X-Webhook-Deliveryで重複排除してください。
配信ログ
Dashboard → Webhooks でエンドポイントを開くと、直近100件の配信を確認できます。各配信について、イベント種別、ステータス(pending、delivered、failed)、サーバーが返したHTTPステータス、レスポンス時間、3回中の使用済み試行回数、作成・完了時刻、pendingの配信の次回リトライ時刻、最後のエラー、送信した完全なペイロードを確認できます。
deliveredまたはfailedの配信には Retry ボタンがあります。同じ配信を再びキューに入れます(X-Webhook-Delivery IDは同じで、新たに署名されます)。試行回数3回の新しい予算が適用されます。APIでは、GET /v1/workspaces/{workspace_id}/webhooks/{id}/deliveriesでlimit(最大100)とoffsetを使い、古い配信をページングできます。POST .../deliveries/{delivery_id}/retryはボタンと同じ処理を行います。自分で配信をリプレイできない場合は、Elidoサポートに依頼できます。
サーバーのレスポンスボディをログに記録する機能は提供予定です。
ローカルでテストする
Webhookをlocalhostへ転送するCLIコマンド(npx @elido/cli webhooks forward)は提供予定です。それまでは、ngrokやCloudflare Tunnelなどのトンネルを通じてローカルサーバーを公開し、そのトンネルのHTTPS URLをエンドポイントとして追加してください。完了したら削除してください。
制限
- エンドポイントが応答するまでの時間は10秒です。
- イベントごとに3回の配信試行があり、手動リトライを追加できます。
- 配信ログAPIが1ページで返す配信は最大100件です。
トラブルシューティング
署名がまったく一致しない。 よくある原因は、フレームワークがハッシュ化する前にJSONを解析して再シリアライズしている(生の本文をハッシュ化してください)、キーからwhsec_プレフィックスを削除している、またはX-Webhook-Timestampヘッダーではなく本文のtimestampで署名していることです。署名検証の記事には、コードを照合できるテストベクターがあります。
ローテーション後に署名が一致しなくなった。 サーバーに古いシークレットが残っています。新しいシークレットをデプロイしてください。2026年9月22日より前に作成されたエンドポイントでは、最初のローテーションにフォールバック用のX-Elido-Signature-Previousがありません。
一部のイベントが2回届く。 エンドポイントがエラーで応答したか、10秒を超えて処理したため、Elidoがリトライしました。X-Webhook-Deliveryで重複排除し、時間のかかる処理を行う前に2xxを返してください。
あるイベント種別がまったく届かない。 エンドポイントがその種別を購読していて、一時停止されていないことを確認してください。また、その種別が上のカタログにあることも確認してください。提供予定と記載された種別はまだ送信されません。