ドキュメント本文へスキップ
PHP

エンドポイント

`webhooks->list`、`listAll`、`iterate`、`get`、`create`、`update`、`delete`、`rotateSecret`、`test`、`getDelivery`、`replayDelivery`、そして配信ログとアクティビティログ。

すべてのメソッド

webhooks.php
use OpenEmail\Constants\WebhookEvents; $endpoint = $client->webhooks->create([    'url' => 'https://acme.com/hooks/mail',    'eventTypes' => [WebhookEvents::EMAIL_SENT, WebhookEvents::EMAIL_BOUNCED],    'description' => 'Billing service',]); file_put_contents('.openemail-webhook-secret', $endpoint['secret']); $client->webhooks->list();$client->webhooks->get($endpoint['id']);$client->webhooks->update($endpoint['id'], ['enabled' => false]);$client->webhooks->test($endpoint['id']); foreach ($client->webhooks->listDeliveries($endpoint['id'], limit: 1) as $latest) {    $client->webhooks->getDelivery($endpoint['id'], $latest['id']);    $client->webhooks->replayDelivery($endpoint['id'], $latest['id']);} $rotated = $client->webhooks->rotateSecret($endpoint['id']);file_put_contents('.openemail-webhook-secret', $rotated['secret']); $client->webhooks->delete($endpoint['id']);

シークレットが返されるのは、rotateSecret を除けば create のときだけである。読み取りでシークレットが返ることはないので、何よりも先に保存すること。eventTypes を省略すると既定のセット、すなわち email.replied を除くすべての email.* イベントになる。email.replied、domain.*、suppression.*、file.*、form.* は、エンドポイントがそれらを明示した場合にのみ届く。

list は OpenEmail\Result\Page を 1 つ返し、listAll はすべてのエンドポイントを 1 つの配列で返し、iterate はエンドポイントを 1 つずつ yield する Generator を返します。create と update は API の名前をキーとする 1 つの配列としてボディを受け取り、エンドポイントはすべて camelCase のキーを持つ配列として返ります。

rotateSecret には移行期間がない。古いシークレットは即座に無効になるため、ローテーションの前に新しいシークレットをデプロイしておくこと。この呼び出しが自動でリトライされることはない。リトライすれば 2 回目のローテーションが起き、1 回目の試行で返されたシークレットが無効になるからである。

create もリトライされないため、ネットワーク障害によって、見ていないシークレットでエンドポイントが作成されたままになることがあります。もう一度作成する前に list を確認してください。ワークスペースは既定で 10 個のエンドポイントを持て、上限を超える次の 1 つは 422 workspace_limit_reached になります。

購読できるイベント

OpenEmail\Constants\WebhookEvents はすべてのイベントを定数として定義し、WebhookEvents::values() がそれらを一覧にするため、リクエストなしで一覧を表示できます。webhooks->listEvents は同じ名前をそれぞれのラベル付きで返し、さらにエンドポイントに課される上限を maxEndpoints、maxAddresses、maxDomains で返します。イベントはこの API のイベントではなく、メールボックスのイベントです。email.received はアプリに届いたメールで発火し、email.sent はコンポーザーが送ったメッセージで発火します。購読は、自分の API のトラフィックを監視することと同じではありません。

file.uploaded はファイルが「ファイル」ページに追加されたときに、file.deleted はファイルが削除されたときに発火します。その data は fileId、filename、mimeType、sizeBytes、direction、to、threadId、messageId、そして uploadedAt または deletedAt を含みます。to はファイルが属するアドレスで、ワークスペース全体に属するファイルなら null になります。

ファイルのイベントは既定のセットに含まれないので、エンドポイントが eventTypes で明示した場合にのみ届く。一部のアドレスに限定されたエンドポイントには、そのアドレスのファイルに関するイベントしか届かない。そのため、to が null のワークスペース全体向けのアップロードは送られない。

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 が再び送られるのは回答が変わったときに限られる。フォームのイベントは既定のセットに含まれず、登録はワークスペース全体に属するため、一部のアドレスに限定されたエンドポイントには届かない。

動作を確認する

webhook_test.php
$result = $client->webhooks->test('whe_3f9c2a7b1e4d8f60a5c7b92d');echo $result['delivery']['status'], ' ', $result['delivery']['responseCode'] ?? 'no response', PHP_EOL; foreach ($client->webhooks->iterateDeliveries('whe_3f9c2a7b1e4d8f60a5c7b92d') as $delivery) {    echo $delivery['eventType'], ' ', $delivery['status'], ' ', $delivery['responseCode'] ?? '-', ' ', $delivery['error'] ?? '', PHP_EOL;}

test は署名付きの合成された email.sent イベントを POST し、試行が終わるまで待ちます。受信側が何を返しても通常どおり戻るため、呼び出しが例外をスローしたかどうかではなく $result['delivery']['status'] で分岐してください。4xx は有益な答えです。URL には到達でき、拒否はあなた自身のハンドラー、多くの場合その署名チェックから来ています。

responseCode が null の場合は応答がまったくなかったこと(DNS、TLS、タイムアウト)を意味し、0 を返した応答とは別の事実です。各行は attempt と maxAttempts を持つため、複数の行が 1 つのイベントを表すことがあります。行をまたいで同一の eventId がイベントを、試行番号が何回目の試行かを表します。nextAttemptAt は、その行に続く自動リトライの予定時刻を示します。

もう一度送る

webhook_replay.php
$detail = $client->webhooks->getDelivery('whe_3f9c2a7b1e4d8f60a5c7b92d', 'whd_8c1e4a7f2b9d3e6a0c5f1b28');echo json_encode($detail['payload'], JSON_THROW_ON_ERROR), PHP_EOL;echo $detail['responseBody'] ?? 'no answer', ' ', $detail['replayRefusal']['code'] ?? 'replayable', PHP_EOL; $replay = $client->webhooks->replayDelivery('whe_3f9c2a7b1e4d8f60a5c7b92d', 'whd_8c1e4a7f2b9d3e6a0c5f1b28');echo $replay['delivery']['status'], ' ', $replay['delivery']['responseCode'] ?? 'no response', PHP_EOL;

失敗し続ける配信は最大 8 回試行される。まず即時、その後 1 分後、5 分後、30 分後、2 時間後、5 時間後、10 時間後、さらに 10 時間後で、合計で約 27 時間半になる。繰り返すのは、繰り返す価値のある失敗だけである。応答なし、408、425、429、5xx がそれにあたる。再送は保存されたイベントを同じ id、type、createdAt、data で送り直すため、処理済みの id を捨てる受信側は、それを既知のイベントとして扱う。新しくなるのは署名だけである。

  • replayDelivery は 1 件のイベントを今すぐ送り、サーバーの応答を返します。配信済みの試行にも使え、リトライされることはありません。送信の前に、そのイベントの自動リトライのうちまだ始まっていないものは一時停止されます:再送が配信されればそれらはキャンセルされたままになり、失敗すれば予定どおり再開します。
  • 同じイベントの自動リトライがちょうど送信中の場合、replayDelivery は何も送らずに 409 retry_in_progress をスローします。別のリプレイがまだ送信中の間は 409 replay_in_progress をスローします。そのため、同時に送られた 2 つのリプレイからであっても、受信側が同時に 2 つのコピーを受け取ることはありません。そのリトライやリプレイで配信されるかもしれないので、数秒待ってから getDelivery を読んでください。リプレイは 1 回に 1 イベントです。失敗したすべての配信をまとめて再送する呼び出しはありません。
  • 無効にされたエンドポイント(webhook_disabled)、エンドポイントがもう購読していないイベント(event_not_subscribed)やもう対象としていないイベント(event_out_of_scope)、保存されたイベントのない試行(delivery_not_replayable)に対しても 409 をスローします。いずれも ConflictException で、OpenEmail\Constants\WebhookReplayErrorCodes がそのコードを定義しています。getDelivery はその答えを replayRefusal として事前に報告します。リプレイが実行される場合は null、そうでなければ code と message を持つ配列です。

パッケージが replayDelivery を自動でリトライすることはありません。応答が失われた後にリトライすれば、イベントがもう一度送られてしまうからです。

パラメーター:webhooks->create

urlstring必須
配信を POST する先。HTTPS のみで、ホストに `localhost`、`.localhost`、`.local`、`.internal` の名前、またはループバック、プライベート、キャリアグレード NAT、リンクローカル、マルチキャスト、ユニークローカルの IP リテラルは使えません。これはあなたが指定したアドレスへのサーバー側からのリクエストなので、それらは `url` に対する 422 `invalid_webhook_url` になります。このチェックは書かれたとおりのホスト名を読み、配信のたびにホストを改めて名前解決し、これらの範囲のアドレスへの送信を拒否します。配信はリダイレクトに従わないため、最終的なアドレスを登録してください。保存されるのは送った値を URL パーサーがシリアライズしたものなので、`https://acme.com` は `https://acme.com/` として読み戻されます。
eventTypesarray
このエンドポイントに届くイベントで、`OpenEmail\Constants\WebhookEvents` の値のいずれかです。`create` は配列の長さを存在するイベントの数までに制限するため、それより 1 つ多いと `eventTypes` に対する 422 になります。`update` には制限がありません。制限されるのは長さだけで、重複した名前も送ったとおりに保存され、読み戻されます。省略するか空にすると空のリストとして保存されるため、`['*']` として読み戻されます。これは `email.replied` を除くすべての `email.*` イベント(現在は 14 個)を意味し、ドメイン、サプレッション、ファイル、フォームの系統は決して含みません。後から追加された系統が、それを指定していないエンドポイントに届くことはないため、リリースによって、連携先が見たことのない形のデータを受け取り始めることはありません。
descriptionstring
エンドポイントのラベルで、最大 200 文字。Webhook の一覧が URL の列ではなく名前として読めるようにするためのものです。省略した場合は null として保存され、null として返ります。null を渡すのではなくキーを省略してください。クライアントは null をそのまま送り、`create` はそれを 422 で拒否します。
addressAllowlistarray
このエンドポイントが通知を受ける個々のアドレス。イベントは、それが関係するアドレスがこのリストにあるか、そのドメインが `domainAllowlist` にある場合に配信されます。両方を空にすると、エンドポイントはワークスペースが所有するすべてのアドレスについて通知を受けます。最大 50 個で、このワークスペースが所有していないアドレスは 422 `invalid_parameter` になります。
domainAllowlistarray
このエンドポイントが通知を受けるドメイン全体で、後から追加されたアドレスも含みます。ドメインは自身の `domain.*` イベントも運びます。最大 25 個です。
apiKeystring
配列の中のキーではなく、配列と並べて渡す名前付き引数です。クライアントのキーではなく、この API キーでエンドポイントを作成します。

レスポンス:作成されたエンドポイント

camelCase のキーを持つ配列。get、list、update は secret を除いた同じ形を返します。

objectstring
常に `webhook` で、通常の読み取りが返すのと同じ判別子です。シークレットは独自のオブジェクト型ではなく、通常の形に 1 つキーが増えただけだからです。`secret` が含まれるかどうかは、このフィールドではなく、どのメソッドを呼んだかによって決まります。
idstring
エンドポイントの識別子:`whe_` に続けて 16 進数 24 文字が並びます。他のすべての Webhook 呼び出しがこの値を受け取ります:`get`、`update`、`delete`、`rotateSecret`、`test`、`listDeliveries`、`listAllDeliveries`、`iterateDeliveries`、`getDelivery`、`replayDelivery`。
urlstring
HTTPS とホストのブロック検査を通過して保存されたエンドポイント。パースされた URL を再度シリアライズしたものであるため、比較には送った文字列ではなくこの値を使ってください。
descriptionstring or null
指定したラベルで、指定しなかった場合は null です。`update` で `'description' => null` を送ると消去されます。
eventTypesarray
購読しているイベント。エンドポイントが何も指定しなかった場合は `['*']`。`['*']` は、保存された空のリストが読み取り時に表現された形であり、送り返すことはできない。これはカタログ全体ではなく、14 種類のメッセージイベントを表す。`create` と `update` が受け付けるのは、実際のイベント名だけである。
enabledbool
配信を試みるかどうか。無効なエンドポイントはイベント配信時にスキップされますが、シークレットと配信履歴は保持されます。`enabled` を受け取るのは `update` だけなので、ここでは常に true です。
disabledAtstring or null
100 回連続で配信に失敗した後に、サーバーがエンドポイントを無効にした時刻。有効な間と、自分で無効にした場合は null です。
disabledReasonstring or null
サーバーが無効にした理由。`disabledAt` が null のときは常に null です。
consecutiveFailuresint
連続した配信失敗の回数。いずれかのイベントが配信されると 0 に戻り、`enabled` を true にした `update` でも 0 に戻ります。
addressAllowlistarray
このエンドポイントが通知を受ける個々のアドレス。
domainAllowlistarray
このエンドポイントが通知を受けるドメイン全体。両方のリストが空なら、ワークスペースが所有するすべてのアドレスを意味します。
lastDeliveryAtstring or null
最後の配信「試行」の ISO 8601 タイムスタンプで、最後に成功した時刻ではありません。POST が失敗した後にも記録されるため、この値はエンドポイントに対して試行が行われたことを示し、その結果は `listDeliveries` が示します。最初の試行までは null なので、`create` では常に null です。
createdAtstring
エンドポイントが登録された時刻の ISO 8601 タイムスタンプ。`list` はこのフィールドの新しい順にエンドポイントを返す。
secretstring
各配信の `X-OpenEmail-Signature` に署名する HMAC-SHA-256 の鍵:`whsec_` に続けて base64url の 43 文字が並び、接頭辞も含めて `OpenEmail::verifyWebhookSignature` に渡す値です。返されるのは `create` と `rotateSecret` だけで、他のどの呼び出しでも返りません。読み取りでは返らないので、その場で保存してください。紛失したシークレットは `rotateSecret` で置き換えるしかなく、その場合は古いものが即座に無効になります。

ログを絞り込む

webhook_logs.php
$failed = $client->webhooks->listWorkspaceDeliveries(status: 'failed', since: new \DateTimeImmutable('-1 day')); foreach ($failed as $delivery) {    echo $delivery['endpointId'], ' ', $delivery['eventType'], ' ', $delivery['responseCode'] ?? '-', PHP_EOL;} $history = $client->webhooks->listActivity('whe_3f9c2a7b1e4d8f60a5c7b92d'); foreach ($history as $change) {    echo $change['type'], ' ', $change['actor']['label'] ?? 'OpenEmail', PHP_EOL;}

listDeliveries は 1 つのエンドポイントを、listWorkspaceDeliveries はすべてのエンドポイント、または endpointIds: で配列かカンマ区切りの 1 つの文字列として指定したものを読みます。どちらもコンソールの「配信」タブのフィルターである status:(delivered または failed)、since:、until: を受け取ります。listActivity と listWorkspaceActivity は監査ログを読みます。誰が何を作成、変更、切り替え、ローテーション、テスト、リプレイ、削除したかです。それぞれに、listAllDeliveries と iterateDeliveries のように listAll と iterate の版があり、ワークスペースのログのすべての行は endpointId を持ちます。webhooks->stats は、指定した期間について「分析」タブの数値を返します。

since: と until: は DateTimeInterface または ISO 8601 の文字列を受け取り、日付だけの文字列はその日の UTC 午前 0 時を意味します。until: は since: より後でなければならず、そうでなければ呼び出しは errorCode が invalid_parameter の InvalidRequestException をスローします。