ナレッジベース
Webhook
ポーリングさせる代わりに、メールの到着をエンドポイントに通知する。
詳細
- 「設定 → Webhook」とAPIから今日すぐ使える。https のエンドポイントを登録し、20あるイベントのうち必要なものを選び、whsec_ の署名シークレットをコピーする。このシークレットは作成時とローテーション時にのみ表示され、以降は二度と表示されない。配信はAPI呼び出しではなくメールボックス自体が発生させる本物の署名付き POST なので、受信メールでも、どこから送られたメッセージであれ開封やクリックでも発火する。送信はすべての経路から発火するようになった。以前は一部からしか発火していなかった。API、MCP、テンプレート、ルール経由の送信は email.sent を発生させたが、アプリ自身の作成画面から送られたメッセージは発生させなかった。作成画面は、イベントを発行する送信サービスを通さずメールボックスに直接書き込んでいたからである。現在このイベントは、すべての経路が合流するメールボックス自体で発生する。したがって、アプリで作成すること、火曜に予約すること、APIに送信することは、同じwebhookを発生させる3つの方法である。遅延送信は2度知らせる。受理時に email.scheduled か email.queued、実際に送信された時点で email.sent、その間に取り消した場合は email.cancelled である。エンドポイントはメールボックスごとに10個までで、この上限はこの画面だけでなく、登録が行われるあらゆる場所で適用される。
- イベントは3つの系統に分かれる。15件は1通のメッセージに関するもので、email.received、email.replied、email.sent、email.delivered、email.failed、email.cancelled、email.scheduled、email.queued(scheduled の、送信取り消しに対応する兄弟イベント)、email.delivery_delayed、email.bounced、email.complained、email.suppressed、email.opened、email.clicked、email.downloaded である。email.sent は送信サービスがメッセージを受理したこと、email.delivered は受信サーバーが受理したこと、email.delivery_delayed はまだ到着しておらず再試行が続いていることを意味する。email.replied は、届いたメッセージがメールボックス内の既存のメッセージへの返信である場合に email.received と同時に発火するので、両方が欲しい利用側は両方を受け取る。email.downloaded は、ダウンロードリンクとして送られたファイルを人が取得したときに発火する。スキャナーやリンクプレビューを数えないための同じ分類器が働き、受信者名は含まれない。リンクはメッセージが送られた全員にとって同一だからである。3件はドメインに関するもので、受信を開始したときの domain.verified、送信の判定が変わったときの domain.sending_changed、削除されたときの domain.deleted があり、最後のものは自分で削除した場合でも、7日で未検証のドメインを回収する処理が削除した場合でも発火する。2件は抑制リスト自体に関するもので、これは email.suppressed とは別物である。アドレスが追加されたときの suppression.added と、再び許可されたときの suppression.removed である。どれも購読しない場合は、email.replied を除くすべてのメッセージイベント、現時点で14件が対象となり、後から追加される系統が含まれることはない。APIはこれを ["*"] として返す。明示したい場合は、欲しいイベントを指定すればよい。各配信には X-OpenEmail-Signature が t=<unix>,v1=<hex> の形式で付き、これはタイムスタンプとドットと生のボディに対するHMAC-SHA-256である。さらに X-OpenEmail-Event と X-OpenEmail-Delivery も付く。検証は届いたままのバイト列に対して行うこと。パースして再シリアライズするとキーの順序が変わり、署名が壊れる。300秒のリプレイ許容範囲は受信側が適用するものであり、SDKの検証機能は既定でこれを用いる。
- https でないもの、および公開経路で到達できないもの(ループバック、RFC1918、リンクローカル、CGNAT、およびそれらのIPv6版)は登録が拒否される。リダイレクトは追跡されないため、3xx は他所へ追いかけるのではなく、失敗した配信として記録される。受信側に与えられる時間は5秒である。エンドポイントへの配信は並列に行われるので、10個あっても50秒ではなく5秒で済む。直近の試行は、応答コードと所要時間とともにそのエンドポイントのページに一覧表示される。
- 配信は最大5回試行される。1回目はイベントの発生と同時に送られ、自然に解消し得る失敗であれば1分後、次に5分後、25分後、2時間後に再試行され、1つのイベントが約2時間半にわたって広がる。再試行はメモリではなく永続的な処理として保持されるため、この時間帯の途中でデプロイが入っても失われない。繰り返す価値のある失敗だけが繰り返される。タイムアウト、接続拒否、408、425、429、および任意の 5xx である。それ以外の 4xx はエンドポイントが意図的にペイロードを拒否しているということであり、さらに4回尋ねても同じ答えのために4倍の負荷がかかるだけである。イベントのidは一度だけ発行され、すべての試行が X-OpenEmail-Delivery でそれを伝えるため、同じidを2度見た受信側は2度処理せずに2件目を捨てられる。100件のイベントが連続してすべての試行に失敗すると、エンドポイントは無効化され、ワークスペースにメールが送られ、理由はエンドポイント自体で確認できる。410 Gone を返すエンドポイントは即座に無効化される。
- 100回連続で失敗したエンドポイントは、いつまでも呼び出し続けるのではなくオフにされ、webhookにアクセスできる全員にその旨がメールで通知される。どのエンドポイントか、最後の試行が何を返したか、そして失敗している間に何もキューに入れられなかったことが伝えられる。カウントは連続回数であり、配信に成功した試行があればリセットされるので、去年3月の不調な午後が積み重なって今日エンドポイントが無効化されることはない。再びオンにすればカウントも消える。コンソールは2つの状態を1つのトグルで見せるのではなく区別する。自分でオフにしたエンドポイントは、こちらがオフにしたものとは見た目が異なる。
- エンドポイントの管理は、入口が2つある1つの仕事である。APIでは POST /webhooks と、その patch、delete、シークレットのローテーション、テスト、配信ログがあり、SDKにはそれぞれに対応するメソッドがある。アプリでは「設定 → Webhook」であり、別のレジストリではなく同じレジストリを操作する。読み取りは webhooks:read で制御されるため、連携を作る人は所有者でなくてもエンドポイントとその配信履歴(どれが発火し、受信側が何を返し、どれだけ時間がかかったか)を見られる。登録、編集、テスト、ローテーション、削除には webhooks:write とメールボックスの所有権の両方が、どちらの入口でも必要である。この後半は意図的なものである。エンドポイントにはアドレスという軸がないため、ワークスペースが持つすべてのアドレスについて、件名と受信者を含むものを受け取る。つまり、権限がないということは「そのすべてを送ってよい」ではないということである。連携を作り、メールは読まないロールは、代わりにワークスペースキーでこれを操作する。