エンドポイント
`webhooks.list`、`list_all`、`iterate`、`get`、`create`、`update`、`delete`、`rotate_secret`、`test`、`get_delivery`、`replay_delivery`、そして配信ログとアクティビティログ。
すべてのメソッド
endpoint = client.webhooks.create( url: "https://acme.com/hooks/mail", eventTypes: ["email.sent", "email.bounced"], description: "Billing service") File.write(".openemail-webhook-secret", endpoint[:secret]) client.webhooks.listclient.webhooks.get(endpoint[:id])client.webhooks.update(endpoint[:id], enabled: false)client.webhooks.test(endpoint[:id])latest = client.webhooks.list_deliveries(endpoint[:id], limit: 1).items.firstclient.webhooks.get_delivery(endpoint[:id], latest[:id])client.webhooks.replay_delivery(endpoint[:id], latest[:id])rotated = client.webhooks.rotate_secret(endpoint[:id])File.write(".openemail-webhook-secret", rotated[:secret])client.webhooks.delete(endpoint[:id])シークレットが返されるのは、rotate_secret を除けば create のときだけです。読み取りでシークレットが返ることはないので、何よりも先に保存してください。eventTypes を省略すると既定のセット、すなわち email.replied を除くすべての email.* イベントになります。email.replied、domain.*、suppression.*、file.*、form.* は、エンドポイントがそれらを明示した場合にのみ届きます。
rotate_secret には移行期間がありません。古いシークレットは即座に無効になるため、ローテーションの前に新しいシークレットをデプロイしておいてください。自動でリトライされることはありません:リトライすれば 2 回目のローテーションが起き、1 回目の試行で返されたシークレットが無効になるからです。
create もリトライされないため、ネットワーク障害によって、見ていないシークレットでエンドポイントが作成されたままになることがあります。もう一度作成する前に list を確認してください。ワークスペースは既定で 10 個のエンドポイントを持て、上限を超える次の 1 つは 422 workspace_limit_reached になります。
購読できるイベント
OpenEmail::WEBHOOK_EVENTS はすべてのイベント名を持つ凍結された Hash なので、リクエストなしで一覧を表示でき、webhooks.list_events は同じ名前をそれぞれの説明文と、エンドポイントに課される上限とともに返します。イベントはこの API のイベントではなく、**メールボックス**のイベントです:email.received はアプリに届いたメールに対して発火し、email.sent はコンポーザーが送信したメッセージに対して発火します。購読することは、自分の API トラフィックを監視することとは違います。
file.uploaded はファイルが「ファイル」ページに追加されたときに、file.deleted はファイルが削除されたときに発火します。その data は fileId、filename、mimeType、sizeBytes、direction、to、threadId、messageId、そして uploadedAt または deletedAt を含みます。to はファイルが属するアドレスで、ワークスペース全体に属するファイルなら nil になります。
ファイルのイベントは既定のセットに含まれないので、エンドポイントが eventTypes で明示した場合にのみ届きます。一部のアドレスに限定されたエンドポイントには、そのアドレスのファイルに関するイベントしか届きません。そのため、to が nil のワークスペース全体向けのアップロードは送られません。
form.submitted は誰かがあなたのフォームのいずれかから登録したときに発火し、form.confirmed は本人が確認リンクを開いたか、あなたが承認したことで、確認待ちの登録がオーディエンスに加わったときに発火します。form.submitted の data は formId、formName、submissionId、email、status、answers、audienceIds、sourceUrl、submittedAt を含みます。form.confirmed の data は formId、formName、submissionId、email、audienceIds、link または approval のいずれかである via、そして confirmedAt を含みます。
ダブルオプトインのないフォームでの登録は、status が added の form.submitted を送り、form.confirmed は送らない。したがって、この組み合わせを誰かが加わった瞬間として扱うこと。確認前にもう一度登録した人の submissionId は変わらず、form.submitted が再び送られるのは回答が変わったときに限られる。フォームのイベントは既定のセットに含まれず、登録はワークスペース全体に属するため、一部のアドレスに限定されたエンドポイントには届かない。
動作を確認する
result = client.webhooks.test("whe_3f9c2a7b1e4d8f60a5c7b92d")puts result.dig(:delivery, :status), result.dig(:delivery, :responseCode) client.webhooks.iterate_deliveries("whe_3f9c2a7b1e4d8f60a5c7b92d") do |delivery| puts "#{delivery[:eventType]} #{delivery[:status]} #{delivery[:responseCode]} #{delivery[:error]}"endtest は署名付きの合成された email.sent イベントを POST し、試行が終わるまで待ちます。受信側が何を返しても通常どおり戻るため、呼び出しが例外を送出したかどうかではなく delivery[:status] で分岐してください。4xx は有益な答えです:URL には到達でき、拒否はあなた自身のハンドラー、多くの場合その署名チェックから来ています。
responseCode が nil の場合は応答がまったくなかったこと(DNS、TLS、タイムアウト)を意味し、0 を返した応答とは別の事実です。各行は attempt と maxAttempts を持つため、複数の行が 1 つのイベントを表すことがあります:行をまたいで同一の eventId がイベントを、試行番号が何回目の試行かを表します。nextAttemptAt は、その行に続く自動リトライの予定時刻を示します。
もう一度送る
detail = client.webhooks.get_delivery("whe_3f9c2a7b1e4d8f60a5c7b92d", "whd_8c1e4a7f2b9d3e6a0c5f1b28")p detail[:payload], detail[:responseBody], detail[:replayRefusal] replay = client.webhooks.replay_delivery("whe_3f9c2a7b1e4d8f60a5c7b92d", "whd_8c1e4a7f2b9d3e6a0c5f1b28")p replay.dig(:delivery, :status), replay.dig(:delivery, :responseCode)失敗し続ける配信は最大 8 回試行される。まず即時、その後 1 分後、5 分後、30 分後、2 時間後、5 時間後、10 時間後、さらに 10 時間後で、合計で約 27 時間半になる。繰り返すのは、繰り返す価値のある失敗だけである。応答なし、408、425、429、5xx がそれにあたる。再送は保存されたイベントを同じ id、type、createdAt、data で送り直すため、処理済みの id を捨てる受信側は、それを既知のイベントとして扱う。新しくなるのは署名だけである。
replay_deliveryは 1 件のイベントを今すぐ送り、サーバーの応答を返します。配信済みの試行にも使え、リトライされることはありません。送信の前に、そのイベントの自動リトライのうちまだ始まっていないものは一時停止されます:再送が配信されればそれらはキャンセルされたままになり、失敗すれば予定どおり再開します。- その瞬間に同じイベントの自動リトライが送信中であれば、
replay_deliveryは何も送らず 409retry_in_progressを送出し、同じイベントの別の再送がまだ送信中であれば 409replay_in_progressを送出します。そのため、同じ瞬間に 2 回再送しても、受信側が同時に 2 通を受け取ることはありません。数秒待ってからget_deliveryを読んでください。そのリトライや再送で配信される場合があります。再送は 1 件ずつです:失敗した配信をすべて再送する呼び出しはありません。 - さらに、無効化されたエンドポイント(
webhook_disabled)、エンドポイントがもう購読していないイベント(event_not_subscribed)やもう対象としていないイベント(event_out_of_scope)、保存されたイベントのない試行(delivery_not_replayable)にも 409 を送出します。get_deliveryはこの結果をreplayRefusalとして事前に知らせます。
gem が replay_delivery を自動でリトライすることはありません。応答が失われた後にリトライすれば、イベントがもう一度送られてしまうからです。
パラメーター: webhooks.create
urlString必須- 配信の POST 先。HTTPS のみで、ホストに `localhost`、`.localhost`・`.local`・`.internal` の名前、ループバック・プライベート・CGNAT・リンクローカルの IP リテラルは指定できません。これは利用者が指定したアドレスに対するサーバー側からのリクエストであるため、これらは `url` に対する 422 `invalid_webhook_url` になります。検査はホスト名を書かれたとおりに読み、配信のたびにホストを改めて名前解決して、これらの範囲のアドレスへの送信を拒否します。配信はリダイレクトをたどらないため、最終的なアドレスを登録してください。保存されるのは、送られた値を URL パーサーがシリアライズしたものです。そのため `https://acme.com` は `https://acme.com/` として読み出されます。
eventTypesArray<String>- このエンドポイントに届くイベント:`OpenEmail::WEBHOOK_EVENTS` に含まれる値のいずれか。`create` は Array の長さを存在するイベント数で制限するため、それを 1 つ超えると `eventTypes` に対する 422 になります。`update` には上限がありません。制限されるのは長さだけで、同じ名前が重複していても、送ったとおりに保存され読み出されます。省略または空の場合は空のリストとして保存され、読み出すと `["*"]` になるのはそのためです。これは `email.replied` を除くすべての `email.*` イベント(現時点で 14 種類)を意味し、ドメイン系、抑止系、ファイル系、フォーム系のイベント群は決して含みません。後から追加されたイベント群が、それを指定していないエンドポイントに届くことはないため、リリースを理由に、インテグレーションが見たことのない形のデータを受け取り始めることはありません。
descriptionString- エンドポイントのラベルで、最大 200 文字。Webhook の一覧が URL の列ではなく名前として読めるようにするためのものです。省略した場合は nil として保存され、nil として返ります。
addressAllowlistArray<String>- このエンドポイントが通知を受ける個々のアドレス。イベントは、それが関係するアドレスがこのリストにあるか、そのドメインが `domainAllowlist` にある場合に配信されます。両方を空にすると、エンドポイントはワークスペースが所有するすべてのアドレスについて通知を受けます。最大 50 個で、このワークスペースが所有していないアドレスは 422 `invalid_parameter` になります。
domainAllowlistArray<String>- このエンドポイントが通知を受けるドメイン全体で、後から追加されたアドレスも含みます。ドメインは自身の `domain.*` イベントも運びます。最大 25 個です。
api_keyString- クライアントのキーではなく、このキーでエンドポイントを作成します。
レスポンス:作成されたエンドポイント
Symbol キーの Hash。get、list、update は secret を除いた同じ形を返します。
objectString- 常に `webhook` で、通常の読み取りが返すのと同じ判別子です。シークレットは独自のオブジェクト型ではなく、通常の形に 1 つキーが増えただけだからです。`secret` が含まれるかどうかは、このフィールドではなく、どのメソッドを呼んだかによって決まります。
idString- エンドポイントの識別子:`whe_` に続けて 16 進数 24 文字が並びます。他のすべての Webhook 呼び出しがこの値を受け取ります:`get`、`update`、`delete`、`rotate_secret`、`test`、`list_deliveries`、`list_all_deliveries`、`iterate_deliveries`、`get_delivery`、`replay_delivery`。
urlString- HTTPS とホストのブロック検査を通過して保存されたエンドポイント。パースされた URL を再度シリアライズしたものであるため、比較には送った String ではなくこの値を使ってください。
descriptionString or nil- 指定したラベルで、指定しなかった場合は nil です。`update` で `description: nil` を送ると消去されます。
eventTypesArray<String>- 購読しているイベントで、エンドポイントが何も指定しなかった場合は `["*"]` です。`["*"]` は、保存された空のリストが読み取り時に表現された形であり、送り返すことはできません。これはカタログ全体ではなく、14 種類のメッセージイベントを表します。`create` と `update` が受け付けるのは、実際のイベント名だけです。
enabledBoolean- 配信を試みるかどうか。無効なエンドポイントはイベント配信時にスキップされますが、シークレットと配信履歴は保持されます。`enabled` を受け取るのは `update` だけなので、ここでは常に true です。
disabledAtString or nil- 100 回連続で配信に失敗した後に、サーバーがエンドポイントを無効にした時刻。有効な間と、自分で無効にした場合は nil です。
disabledReasonString or nil- サーバーが無効にした理由。`disabledAt` が nil のときは常に nil です。
consecutiveFailuresInteger- 連続した配信失敗の回数。いずれかのイベントが配信されると 0 に戻り、`enabled: true` を指定した `update` でも 0 に戻ります。
addressAllowlistArray<String>- このエンドポイントが通知を受ける個々のアドレス。
domainAllowlistArray<String>- このエンドポイントが通知を受けるドメイン全体。両方のリストが空なら、ワークスペースが所有するすべてのアドレスを意味します。
lastDeliveryAtString or nil- 最後の配信「試行」の ISO 8601 タイムスタンプで、最後に成功した時刻ではありません。POST が失敗した後にも記録されるため、この値はエンドポイントに対して試行が行われたことを示し、その結果は `list_deliveries` が示します。最初の試行までは nil なので、`create` では常に nil です。
createdAtString- エンドポイントが登録された時刻の ISO 8601 タイムスタンプ。`list` はこのフィールドの新しい順にエンドポイントを返す。
secretString- 各配信の `X-OpenEmail-Signature` に署名する HMAC-SHA-256 の鍵:`whsec_` に続けて base64url の 43 文字が並び、接頭辞も含めて `OpenEmail.verify_webhook_signature` に渡す値です。返されるのは `create` と `rotate_secret` だけで、他のどの呼び出しでも返りません。読み取りでは返らないので、その場で保存してください。紛失したシークレットは `rotate_secret` で置き換えるしかなく、その場合は古いものが即座に無効になります。
ログを絞り込む
failed = client.webhooks.list_workspace_deliveries(status: "failed", since: Time.now - 86_400)p failed.items.map { |delivery| [delivery[:endpointId], delivery[:eventType], delivery[:responseCode]] } history = client.webhooks.list_activity("whe_3f9c2a7b1e4d8f60a5c7b92d")p history.items.map { |change| [change[:type], change.dig(:actor, :label)] }list_deliveries は 1 つのエンドポイントを、list_workspace_deliveries はすべてのエンドポイントまたは endpoint_ids: で指定したものを読み、どちらもコンソールの「配信」タブのフィルターと同じ status:、since:、until: を受け取ります。list_activity と list_workspace_activity は監査ログを読みます:誰が何を作成・変更・無効化または有効化・ローテーション・テスト・再送・削除したか。それぞれに list_all_ と iterate_ の版があり、ワークスペースのログの各行には endpointId が付きます。webhooks.stats は、任意の期間について「分析」タブの元になる数値を返します。
since: と until: は Time、DateTime、または String の ISO 8601 時刻を受け取り、Ruby の Date はその日の UTC 午前 0 時を意味します。until は Ruby のキーワードですが、他と同じようにキーワード引数として使えます:list_deliveries(id, since: start, until: finish)。