webhookとは?
執筆者 Uttam Singh

webhookとは、HTTP POSTとして届けられるイベント通知です。あるシステムで何かが起きると、そのシステムはあなたが管理するURLにリクエストを送り、そのリクエストボディに何が起きたかが記述されています。アプリ側から変更の有無を問い合わせる必要はありません。
ブロックチェーンアプリでは、イベントはオンチェーンで発生します。ユーザーのウォレットがUSDCを受け取る、NFTの所有者が変わる、デプロイしたコントラクトがログを発行する、といったものです。webhookがなければ、バックエンドはタイマーでノードやAPIをポーリングし、結果を比較してこうした変化を検出することになります。webhookがあれば、変化は検出され次第サーバーに送られます。
このガイドでは、webhook URL、エンドポイント、ペイロードとは何か、webhookとAPIの違い、配信とリトライの仕組み、リクエストの検証方法、そして本番環境での重複とreorgへの対処方法を解説します。
webhookとは?
webhookは2つの当事者をつなぎます。送信者はイベントを監視します。受信者は、URLを公開しているあなたのアプリです。購読した条件にイベントが一致すると、送信者はそのURLに対し、イベントを記述したJSONボディを付けてHTTP POSTリクエストを送ります。あなたのサーバーは200ステータスコードで応答し、受信したことを伝えます。あなたの側からデータが返ることはないため、webhookは一方向のサーバー間メッセージです。
代替手段はポーリングで、これは決まった間隔で情報源に更新を問い合わせる方法です。そうしたリクエストの大半は新しい情報なしで返ってきます。また、イベントはアプリに認識されるまで、最大でポーリング間隔1回分、気づかれないままになることがあります。webhookは報告すべきイベントがあるときにだけリクエストが送られるため、この2つの問題をどちらも回避できます。
ポーリングは、注文した商品が届いたかを確認するために1時間ごとに店に電話をかけるようなものです。webhookは、商品が届いたときに店からSMSが届くようなものです。
webhook URLとは?
webhook URLとは、送信者がイベントを届ける公開アドレスで、たとえばhttps://api.example.com/webhooks/alchemyのようなものです。webhookを作成する際に、このURLをプロバイダーに登録します。リクエストを送るのはプロバイダーのサーバーなので、URLはインターネットから到達可能でなければなりません。また、ペイロードにはユーザーのアクティビティが記述されるため、HTTPSを使うべきです。開発中は、ngrokのようなトンネリングツールを使うと、ノートPC上のサーバーに一時的な公開URLを割り当てられます。この設定手順はwebhookクイックスタートで説明しています。
webhookエンドポイントとは?
webhookエンドポイントとは、webhook URLに送られたリクエストを処理する、サーバー上のルートです。この2つの用語は同じ意味で使われることがよくあります。URLはアドレスで、エンドポイントはその背後にあるコードです。
エンドポイントの役割は小さく保ちます。リクエストがプロバイダーから送られたものかを確認し、イベントを保存して、200を返します。残高の更新やプッシュ通知の送信など、時間のかかる処理はバックグラウンドワーカーで行います。そうした処理をリクエストの中で実行すると、データベース呼び出しが遅いだけで、プロバイダーが配信に失敗したと判断することがあります。
webhookペイロードとは?
ペイロードとは、リクエストのJSONボディです。当社のwebhookはすべて同じエンベロープを共有しており、eventフィールドにそのwebhookタイプのデータが入ります。以下は、ウォレットアドレスのリストを監視するタイプであるAddress Activity webhookから送られた、Ethereum上のUSDC転送のペイロードです。長さの都合で一部のフィールドを省略しています。
{
"webhookId": "wh_k63lg72rxda78gce",
"id": "whevt_vq499kv7elmlbp2v",
"createdAt": "2024-01-23T07:42:26.411977228Z",
"type": "ADDRESS_ACTIVITY",
"event": {
"network": "ETH_MAINNET",
"activity": [
{
"blockNum": "0xdf34a3",
"hash": "0x7a4a39da2a3fa1fc2ef88fd1eaea070286ed2aba21e0419dcfb6d5c5d9f02a72",
"fromAddress": "0x503828976d22510aad0201ac7ec88293211d23da",
"toAddress": "0xbe3f4b43db5eb49d1f48f53443b9abce45da3b79",
"value": 293.092129,
"asset": "USDC",
"category": "token",
"rawContract": {
"address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"decimals": 6
},
"log": {
"logIndex": "0x6e",
"removed": false
}
}
]
}
}webhookIdはどのwebhookが発火したかを示し、idはこのイベントを識別し、typeはwebhookタイプを表します。activityの各エントリは、監視対象のアドレスに関わった1件の転送です。トークンを特定するには、トークンのコントラクトアドレスであるrawContract.addressを使います。assetのシンボルはトークンコントラクト自体から取得されるもので、USDCを名乗るトークンは誰でもデプロイできます。
webhookはAPIとどう違うのですか?
APIは呼び出されるのを待ちます。アプリがリクエストを送り、データを受け取ります。webhookはこの向きを逆にし、報告すべきことがあるときにプロバイダーがアプリを呼び出します。この逆転があるため、webhookは「逆向きのAPI」と呼ばれることがあります。
本番環境のアプリの多くは両方を使います。webhookは何かが起きたことを伝え、残りの情報はAPI呼び出しで補います。たとえば決済バックエンドは、入金のwebhookを受け取った後、Token APIを呼び出してウォレットの現在の残高を読み取り、それをユーザーに表示します。
webhookは、受け取る側がサーバーエンドポイントの場合に適しています。受け取る側がトレーディングボットやライブダッシュボードのような長時間稼働するプロセスであれば、代わりにWebSocketサブスクリプションを使うと、1本の接続を開いたままその上でイベントをストリーミングできます。どちらを選ぶべきかは、webhook、WebSocket、gRPCの比較で解説しています。
webhookはどのように機能しますか?
webhookの配信は、毎回同じ段階を経て進みます。
- 購読する。送信先URLとフィルターを指定してwebhookを作成します。フィルターでは、監視するネットワークと、アドレスのリストやコントラクトのイベントなど、そのネットワーク上で監視する対象を指定します。
- イベントが一致する。新しいブロックに、フィルターに一致するアクティビティが含まれます。
- プロバイダーがPOSTを送る。リクエストには、ペイロードと、サーバーで検証できる署名ヘッダーが含まれます。
- サーバーが受信を確認する。200レスポンスによって、そのイベントは配信済みになります。
- 失敗した配信はリトライされる。サーバーがダウンしている、タイムアウトする、またはそれ以外のステータスを返した場合、プロバイダーは後で再試行します。
Alchemyのリトライには指数バックオフを使用しており、失敗するたびに試行間の待ち時間が長くなります。webhookクイックスタートの配信動作の項で説明しているとおり、FreeプランとPay As You Goプランでは最大10分間、Enterpriseプランでは最大1時間リトライを続けます。
初回の通知は、ブロック生成速度にかかわらず、すべてのチェーンで順序どおりに配信されます。この保証は初回の試行にのみ適用されるため、リトライされたイベントがより新しいイベントの後に届くことがあります。各イベントとともにブロック番号を保存し、到着時刻ではなくブロック番号でレコードを並べてください。
リトライ期間を過ぎてもサーバーがダウンしたままの場合、その期間のイベントは再配信されないため、チェーンの履歴から欠落を埋めます。Ethereumと、Transfers APIが対応するL2では、取りこぼしたアドレスとブロック範囲を指定してTransfers APIにクエリを送ります。Stellarでは、Stellarの転送エンドポイントが、ledger範囲で絞り込んだアカウントの転送を返します。Solanaでは、getSignaturesForAddressを使って、監視している各アドレスのトランザクションを過去にさかのぼってページングします。
ブロックチェーンアプリでwebhookは何に使われますか?
当社は4種類のwebhookを提供しており、どれが適しているかは監視したい対象によって決まります。
Address Activity webhookは、最も一般的な用途であるウォレット群の監視に対応します。決済バックエンドは自社の入金アドレスを監視し、USDCが届いたらユーザーの残高に反映します。ウォレットアプリは、ユーザーがトークンを受け取ったときにプッシュ通知を送ります。ERC-20の転送にはトークンのコントラクトアドレスが含まれており、決済バックエンドはこれを使って入金が本物のUSDCであることを確認します。
NFT Activity webhookはNFTコントラクトに対して同じことを行うもので、マーケットプレイスやコレクションのダッシュボードに適しています。それ以外のすべてはCustom webhookで対応できます。特定のイベントシグネチャを持つ自分のコントラクトのログなど、必要なブロックデータを選択するGraphQLクエリを書くと、webhookは新しいブロックごとにそのデータだけを載せて発火します。Stellar Address Activity webhookは、Sorobanコントラクトのイベントを含め、Stellarでのアドレス追跡を可能にします。
各webhookが監視するのは1つのネットワークです。複数のチェーンをカバーするには、ネットワークごとにwebhookを1つずつ作成し、すべて同じURLに向けます。webhookはオンチェーンAIエージェントにも適しています。エージェントはループでポーリングする代わりに、監視対象のウォレットが資金を動かしたときに起動できます。
webhookの署名はどのように検証しますか?
webhook URLを見つけた人は誰でもそこにリクエストを送れるため、エンドポイントは各リクエストを処理する前に、それがプロバイダーから送られたものであることを確認する必要があります。当社はすべてのwebhookリクエストにHMAC-SHA256で署名しています。HMAC-SHA256は鍵付きハッシュで、秘密鍵を持つ者だけが同じ値を再現できます。鍵はwebhookの署名鍵、入力はリクエストボディで、16進数の結果がX-Alchemy-Signatureヘッダーに入ります。サーバーは受信したボディに対して同じハッシュを計算し、2つの値を比較します。
署名鍵はwebhookごとに異なり、Alchemyダッシュボードのそのwebhookのページに表示されます。この検証が実際に保護として機能するかどうかは、次の3つの点で決まります。
- 生のバイト列をハッシュする。署名は、送信されたとおりのボディを対象としています。フレームワークがJSONを解析し、コードがそれを再度シリアライズすると、空白やキーの順序が変わることがあり、ハッシュが一致しなくなります。
- 定数時間で比較する。
===や==ではなく、Nodeではcrypto.timingSafeEqual、Pythonではhmac.compare_digestを使います。こうすることで、偽造された署名のどこまでが正しかったかがレスポンスのタイミングから漏れません。 - 何よりも先に拒否する。401を返して処理を止めます。検証していないペイロードを解析したり保存したりしてはいけません。
以下は、この3点すべてに従った最小限のExpressエンドポイントです。
import express from "express";
import { createHmac, timingSafeEqual } from "node:crypto";
// enqueue is your own durable queue (a database table, SQS, BullMQ).
// It should resolve only after the event is safely stored.
import { enqueue } from "./queue";
const signingKey = process.env.ALCHEMY_WEBHOOK_SIGNING_KEY ?? "";
if (!signingKey) throw new Error("Set ALCHEMY_WEBHOOK_SIGNING_KEY");
function isValidSignature(rawBody: Buffer, signature: string | undefined): boolean {
if (!signature) return false;
const expected = createHmac("sha256", signingKey).update(rawBody).digest();
const received = Buffer.from(signature, "hex");
return received.length === expected.length && timingSafeEqual(received, expected);
}
const app = express();
// express.raw() keeps the exact bytes Alchemy signed, so don't parse JSON on this route first.
app.post("/webhooks/alchemy", express.raw({ type: "*/*" }), async (req, res) => {
if (!isValidSignature(req.body, req.get("x-alchemy-signature"))) {
res.sendStatus(401);
return;
}
await enqueue(JSON.parse(req.body.toString("utf8")));
res.sendStatus(200);
});
app.listen(8080);第2の防御層として、ファイアウォールで当社のwebhook IPアドレスのみを許可することもできます。これらのアドレスはwebhookクイックスタートのセキュリティの項に記載されています。
重複、遅延、reorgされたwebhookをアプリはどう処理すべきですか?
webhookの配信は「少なくとも1回」として扱います。タイムアウト後のリトライによって、サーバーがすでに保存したイベントが再び届くことがあるため、ハンドラーは冪等でなければなりません。同じイベントを2回処理しても、1回処理した場合と同じ結果になる必要があります。
- リクエスト単位ではなく転送単位で重複を排除する。1つのペイロードに複数の転送が含まれることがあり、1つのトランザクションに複数の転送が含まれることもあります。ネットワーク、トランザクションの
hash、トランザクション内での転送の位置からキーを作成します。転送の位置は、トークン転送ではlog.logIndex、内部転送ではtypeTraceAddressです。外部転送は1つのトランザクションに1件しかないため、ハッシュだけで足ります。キーを一意制約付きのテーブルに保存し、キーとlog.removedの値の両方が保存済みのレコードと一致する場合にのみ、その配信をスキップします。 - 先に保存し、その後で受信を確認する。イベントを永続的に保存したら200を返し、実際の処理はワーカーで行います。応答する前に処理を行うハンドラーはタイムアウトのリスクを抱え、タイムアウトするとすでに処理済みかもしれないイベントのリトライが発生します。
- reorgされた転送を取り消す。チェーンの再編成、つまりreorgは直近のブロックを置き換え、置き換えられたブロック内のトランザクションはチェーンから外れます。トークン転送には
log.removedフィールドがあり、trueはそのトランザクションがもはや正規チェーンに含まれていないことを意味します。log.removedがtrueに設定された配信は元の転送と同じキーを持つため、保存済みのレコードに対する更新として扱います。レコードを除外済みとしてマークし、その転送に対してアプリが行った処理をすべて元に戻します。 - 取り消せない処理の前にファイナリティを待つ。保留中の入金をすぐに表示するのは妥当です。一方、資金の解放、商品の発送、引き出し可能な残高への入金反映は、ブロックがファイナライズされるまで待つべきです。Ethereumでは、
finalizedブロックタグによって、どのブロックがその段階に達したかがわかります。
Alchemyでwebhookを設定するにはどうすればよいですか?
まずはリスナーから始めます。webhookクイックスタートには署名検証が組み込まれたJavaScript、Python、Go、Rustのスターターサーバーがあり、webhookサンプルリポジトリにはローカルで実行できる完全なプロジェクトがあります。
次にwebhookを作成します。最も手早いのはwebhookダッシュボードを使う方法です。タイプとネットワークを選び、アドレスまたはフィルターを追加し、URLを貼り付けて、Test Webhookボタンでエンドポイントにサンプルイベントを送ります。
コードからwebhookを作成するには、webhookダッシュボードの上部に表示される認証トークンを使ってNotify APIを呼び出します。
curl -X POST https://dashboard.alchemy.com/api/create-webhook \
-H "X-Alchemy-Token: $ALCHEMY_WEBHOOK_AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"network": "BASE_MAINNET",
"webhook_type": "ADDRESS_ACTIVITY",
"webhook_url": "https://api.example.com/webhooks/alchemy",
"addresses": ["0xYOUR_DEPOSIT_ADDRESS"]
}'アドレスは後から同じAPIで追加・削除できるため、新規ユーザーごとに入金アドレスを作成するアプリでも、何千ものwebhookを作成せずに1つのwebhookで済みます。Alchemy CLIは同じAPIをalchemy webhook create、list、update、deleteとしてラップしており、コーディングエージェントにwebhookを管理させる場合に便利です。
Alchemyダッシュボードの動作を見る
30分のガイド付きウォークスルーで、ダッシュボードの全ツールをライブでデモします。
Watch the demoAlchemyでwebhookを使い始める
Alchemy WebhooksはFreeプランで利用でき、契約や最低利用額の確約は不要です。最初のwebhookとしては、自分が管理するウォレットに対するAddress Activity webhookが適しています。そのウォレットに少額を送金し、ペイロードがエンドポイントに届くのを確認してください。その次は、Custom webhookを使えば、同じ配信、リトライ、署名の仕組みで自分のコントラクトのイベントを監視できます。
よくある質問
webhookを簡単に説明すると何ですか?
webhookとは、何かが起きたときにあるアプリから別のアプリへ自動的に送られるメッセージです。送信側のアプリは、あなたが指定したURLにHTTP POSTリクエストを送り、イベントの詳細をリクエストボディに含めます。更新のほうからアプリに届くため、アプリが更新を問い合わせ続ける必要はありません。
webhookとAPIの違いは何ですか?
APIでは、アプリがリクエストを送り、サーバーがデータで応答します。webhookでは、イベントが発生するとサーバーが求められることなくアプリにリクエストを送ります。APIは必要なときにデータを読み取る用途に、webhookはイベントに反応する用途に適しています。多くのアプリは両方を使い、webhookを受け取ってから、詳細を得るためにAPIを呼び出します。
ウォレットにUSDCが届いたときにwebhook通知を受け取るにはどうすればよいですか?
USDCを受け取るチェーンでAlchemyのAddress Activity webhookを作成し、ウォレットアドレスを追加して、送信先をサーバーのURLに設定します。受信する転送ごとに、送信者、金額、トークンのコントラクトアドレスが届きます。その名前のトークンは誰でもデプロイできるため、「USDC」というシンボルではなく、USDCのコントラクトアドレスで照合してください。
Solanaウォレットのアクティビティについてwebhook通知を受け取るにはどうすればよいですか?
AlchemyのAddress Activity webhookは30以上のEVMチェーンに加えてSolanaにも対応しているため、Solanaのウォレットアドレス群を監視し、アクティビティがあったときにPOSTリクエストを受け取れます。Address Activity webhookのドキュメントから、Solana専用のセットアップガイドにアクセスできます。各リクエストは、処理する前に署名を検証してください。
スマートコントラクトがイベントを発行したときにwebhookで通知を受け取れますか?
はい。AlchemyのCustom webhookはGraphQLクエリを受け付けるため、コントラクトのアドレスから出された、特定のイベントシグネチャを持つログに絞り込めます。webhookは新しいブロックごとに、一致したログだけを載せて発火します。各Custom webhookが監視するのは1つのネットワークなので、コントラクトをデプロイしたチェーンごとに1つずつ作成してください。
webhookが発火したときにサーバーがダウンしていたらどうなりますか?
Alchemyは失敗した配信を指数バックオフでリトライします。リトライ期間は、FreeプランとPay As You Goプランでは最大10分、Enterpriseプランでは最大1時間です。サーバーが200を返さない場合、その配信は失敗となります。サーバーのダウンがそれより長く続いた場合は、Ethereumと対応するL2ではTransfers APIを、SolanaとStellarでは各チェーン独自の履歴エンドポイントを使って、チェーンの履歴から取りこぼしたアクティビティを補完してください。
1つのwebhookで追跡できるアドレスはいくつですか?
AlchemyのAddress Activity webhookは最大100,000件のアドレスを追跡でき、Stellar Address Activity webhookも同様です。Notify APIを使えばwebhookを作り直さずにアドレスを追加・削除できるため、ユーザーごとに新しい入金アドレスを作成するアプリに適しています。
関連する概要

ブロックチェーンで魔法を生み出す
Alchemyは、最も強力なweb3開発者向けプロダクトとツールを、豊富なリソース、コミュニティ、そして卓越したサポートと組み合わせて提供します。


