پرش به مستندات
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']);

create جز rotateSecret تنها جایی است که کلید مخفی بازگردانده می‌شود. خواندن هرگز آن را بازتاب نمی‌دهد، پس پیش از هر کار دیگری ذخیره‌اش کنید. برای مجموعهٔ پیش‌فرض، یعنی هر رویداد email.* جز email.replied، eventTypes را ندهید. email.replied، domain.*، suppression.*، file.* و form.* تنها وقتی به یک اندپوینت می‌رسند که آن اندپوینت نامشان را ببرد.

list یک OpenEmail\Result\Page برمی‌گرداند، listAll همهٔ اندپوینت‌ها را در یک آرایه برمی‌گرداند، و iterate یک Generator برمی‌گرداند که هر بار یک اندپوینت را yield می‌کند. create و update بدنه را به‌صورت یک آرایه با نام‌های API می‌گیرند، و هر اندپوینت به‌صورت یک آرایه با کلیدهای camelCase برمی‌گردد.

rotateSecret هیچ پنجرهٔ هم‌پوشانی ندارد. کلید مخفی قدیمی بی‌درنگ از کار می‌افتد، پس پیش از چرخاندن، کلید تازه را مستقر کنید. هرگز خودکار دوباره تلاش نمی‌شود: تلاش مجدد بار دوم می‌چرخاند و کلید مخفی‌ای را که تلاش اول برگردانده بود باطل می‌کند.

create هم دوباره امتحان نمی‌شود، پس یک شکست شبکه ممکن است اندپوینتی ساخته‌شده با secretی که هرگز ندیده‌اید باقی بگذارد. پیش از ساختن دوباره، list را بررسی کنید. هر فضای کاری به‌طور پیش‌فرض 10 اندپوینت دارد، و اندپوینت بعدی پس از این سقف یک 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 وقتی یک ثبت‌نامِ در انتظار به گروه‌های مخاطب می‌پیوندد، چون شخص پیوند تأیید را باز کرده یا شما آن را تأیید کرده‌اید. data در form.submitted شامل formId، formName، submissionId، email، status، answers، audienceIds، sourceUrl و submittedAt است. data در form.confirmed شامل formId، formName، submissionId، email، audienceIds، via، که link یا approval است، و confirmedAt است.

ثبت‌نام در فرمی بدون تأیید دوگانه، form.submitted را با status برابر added می‌فرستد و 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 دارد، پس چند ردیف می‌توانند یک رویداد را توصیف کنند: 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 دوباره می‌فرستد، پس گیرنده‌ای که شناسه‌های پردازش‌شده را کنار می‌گذارد آن را همان رویدادی می‌داند که می‌شناسد. فقط امضا تازه است.

  • replayDelivery یک رویداد را همین حالا می‌فرستد و آنچه سرور شما پاسخ داده را برمی‌گرداند. روی تلاشی که تحویل شده هم کار می‌کند و هرگز دوباره امتحان نمی‌شود. پیش از فرستادن، تلاش‌های دوبارهٔ خودکار آن رویداد که هنوز آغاز نشده‌اند متوقف می‌شوند: وقتی بازپخش تحویل شود لغوشده می‌مانند، و وقتی شکست بخورد طبق زمان‌بندی خودشان از سر گرفته می‌شوند.
  • اگر در همان لحظه یک تلاش دوبارهٔ خودکار از همان رویداد در حال فرستاده شدن باشد، replayDelivery چیزی نمی‌فرستد و یک 409 retry_in_progress را پرتاب می‌کند، و تا وقتی بازپخش دیگری از آن هنوز در حال فرستاده شدن است یک 409 replay_in_progress را پرتاب می‌کند، تا گیرندهٔ شما هرگز دو نسخه را هم‌زمان نگیرد، حتی از دو بازپخش که در یک لحظه فرستاده شده‌اند. چند ثانیه صبر کنید و getDelivery را بخوانید، چون آن تلاش دوباره یا بازپخش ممکن است آن را تحویل دهد. بازپخش هر بار برای یک رویداد است: هیچ فراخوانی‌ای همهٔ تحویل‌های ناموفق را دوباره نمی‌فرستد.
  • همچنین برای اندپوینت خاموش‌شده (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`، یا یک IP صریح از نوع loopback، خصوصی، NAT در سطح اپراتور، link-local، multicast یا unique local باشد. این درخواستی سمت سرور به نشانی‌ای است که شما می‌دهید، پس این‌ها یک 422 `invalid_webhook_url` روی `url` هستند. بررسی نام میزبان را همان‌طور که نوشته شده می‌خواند، و هر تحویل میزبان را دوباره جست‌وجو می‌کند و از فرستادن به نشانی‌ای در یکی از آن بازه‌ها خودداری می‌کند. تحویل‌ها هرگز هدایت‌ها را دنبال نمی‌کنند، پس نشانی نهایی را ثبت کنید. آنچه ذخیره می‌شود سریال‌سازیِ تجزیه‌گر URL از چیزی است که فرستاده‌اید، پس `https://acme.com` به شکل `https://acme.com/` خوانده می‌شود.
eventTypesarray
کدام رویدادها به این اندپوینت می‌رسند: هر یک از مقدارهای `OpenEmail\Constants\WebhookEvents`. `create` اندازهٔ آرایه را به تعداد رویدادهای موجود محدود می‌کند، پس یکی بیشتر از آن یک 422 روی `eventTypes` است، و `update` آن را محدود نمی‌کند. فقط طول محدود می‌شود، و نام تکراری دقیقاً همان‌طور که فرستاده‌اید ذخیره و خوانده می‌شود. اگر ننویسید یا خالی باشد، به‌صورت فهرستی خالی ذخیره می‌شود، و به همین دلیل به شکل `['*']` خوانده می‌شود، و یعنی همهٔ رویدادهای `email.*` جز `email.replied`، امروز چهارده رویداد، و هرگز خانواده‌های دامنه، توقیف، فایل یا فرم. خانواده‌ای که بعدها افزوده شود هرگز به اندپوینتی که نامش را نبرده نمی‌رسد، پس یک یکپارچه‌سازی نمی‌تواند به خاطر یک انتشار، دریافت شکلی را آغاز کند که هرگز ندیده است.
descriptionstring
برچسبی برای اندپوینت، حداکثر 200 نویسه، تا فهرستی از وب‌هوک‌ها به‌صورت نام‌ها خوانده شود نه ستونی از URLها. اگر ننویسید، به‌صورت null ذخیره و برگردانده می‌شود. به‌جای دادن null این کلید را ننویسید: کلاینت null را همان‌طور که هست می‌فرستد، و `create` آن را با یک 422 رد می‌کند.
addressAllowlistarray
نشانی‌های تکی که این اندپوینت دربارهٔ آن‌ها خبر می‌گیرد. رویدادی تحویل می‌شود که نشانی مربوط به آن در این فهرست باشد، یا دامنه‌اش در `domainAllowlist`. هر دو را خالی بگذارید تا اندپوینت دربارهٔ همهٔ نشانی‌هایی که فضای کاری مالک آن‌هاست خبر بگیرد. حداکثر 50، و نشانی‌ای که این فضای کاری مالکش نیست یک 422 `invalid_parameter` است.
domainAllowlistarray
دامنه‌های کاملی که این اندپوینت دربارهٔ آن‌ها خبر می‌گیرد، از جمله نشانی‌هایی که بعداً به آن‌ها افزوده می‌شوند. یک دامنه رویدادهای `domain.*` خودش را هم می‌آورد. حداکثر 25.
apiKeystring
یک آرگومان نام‌دار در کنار آرایه، نه کلیدی درون آن: اندپوینت را به‌جای کلید کلاینت با این کلید API می‌سازد.

پاسخ: اندپوینت ساخته‌شده

یک آرایه با کلیدهای camelCase. get، list و update همین شکل را بدون secret برمی‌گردانند.

objectstring
همیشه `webhook`، همان تمایزدهنده‌ای که یک خواندن ساده برمی‌گرداند، چون secret یک کلید اضافه روی همان شکل معمولی است نه یک نوع شیء جداگانه. اینکه `secret` حاضر باشد یا نه را متدی که فراخوانده‌اید تعیین می‌کند، نه این فیلد.
idstring
شناسهٔ اندپوینت: `whe_` و به دنبالش 24 نویسهٔ hex. هر فراخوانی دیگر وب‌هوک آن را می‌گیرد: `get`، `update`، `delete`، `rotateSecret`، `test`، `listDeliveries`، `listAllDeliveries`، `iterateDeliveries`، `getDelivery` و `replayDelivery`.
urlstring
اندپوینت همان‌طور که ذخیره شده، پس از گذر از بررسی‌های HTTPS و میزبان‌های مسدود. همان URL تجزیه‌شده است که دوباره سریال‌سازی شده، پس با این مقدار مقایسه کنید نه با رشته‌ای که فرستاده‌اید.
descriptionstring or null
برچسبی که به آن داده‌اید، یا null اگر چیزی نداده باشید. `update`ی که `'description' => null` بفرستد آن را پاک می‌کند.
eventTypesarray
رویدادهای مشترک‌شده، یا وقتی اندپوینت هیچ‌کدام را نام نبرده باشد `['*']`. `['*']` شیوهٔ رندر شدن یک فهرست ذخیره‌شدهٔ خالی هنگام خواندن است و نمی‌توان آن را بازفرستاد، و نمایندهٔ چهارده رویداد پیام است نه کل فهرست. `create` و `update` تنها نام‌های واقعی رویدادها را می‌پذیرند.
enabledbool
اینکه آیا تحویل‌ها تلاش می‌شوند یا نه. اندپوینت غیرفعال هنگام روانه شدن رویدادها نادیده گرفته می‌شود و secret و تاریخچهٔ تحویلش را نگه می‌دارد. اینجا همیشه true است، چون فقط `update` مقدار `enabled` را می‌گیرد.
disabledAtstring or null
زمانی که سرور پس از 100 تحویل ناموفق پیاپی اندپوینت را خاموش کرد. تا وقتی روشن است null است، و همچنین وقتی خودتان آن را خاموش کرده‌اید.
disabledReasonstring or null
اینکه چرا سرور آن را خاموش کرد. هر وقت `disabledAt` برابر null باشد null است.
consecutiveFailuresint
تحویل‌های ناموفق پیاپی. هر رویداد تحویل‌شده آن را به 0 بازمی‌گرداند، و `update` با `enabled` برابر true هم همین‌طور.
addressAllowlistarray
نشانی‌های تکی که این اندپوینت دربارهٔ آن‌ها خبر می‌گیرد.
domainAllowlistarray
دامنه‌های کاملی که این اندپوینت دربارهٔ آن‌ها خبر می‌گیرد. خالی بودن هر دو فهرست یعنی همهٔ نشانی‌هایی که فضای کاری مالک آن‌هاست.
lastDeliveryAtstring or null
مهر زمانی ISO 8601 آخرین «تلاش» تحویل، نه آخرین موفقیت. پس از یک POST ناموفق هم ثبت می‌شود، پس به شما می‌گوید اندپوینت امتحان شده و `listDeliveries` می‌گوید نتیجه چه بوده. تا نخستین تلاش null است، و بنابراین روی `create` همیشه null است.
createdAtstring
مهر زمانی ISO 8601 زمانی که اندپوینت ثبت شده است. `list` اندپوینت‌ها را بر اساس همین فیلد از تازه‌ترین بازمی‌گرداند.
secretstring
کلید HMAC-SHA-256 که `X-OpenEmail-Signature` هر تحویل را امضا می‌کند: `whsec_` و به دنبالش 43 نویسهٔ base64url، و همان چیزی که با پیشوندش به `OpenEmail::verifyWebhookSignature` می‌دهید. فقط `create` و `rotateSecret` آن را برمی‌گردانند و هیچ چیز دیگری. هیچ خواندنی آن را بازنمی‌گرداند، پس همین حالا ذخیره‌اش کنید. secret گم‌شده را فقط می‌توان با `rotateSecret` جایگزین کرد، که secret قدیمی را بی‌درنگ باطل می‌کند.

فیلتر کردن گزارش‌ها

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 یک اندپوینت را می‌خواند و listWorkspaceDeliveries همهٔ اندپوینت‌ها، یا آن‌هایی را که endpointIds: نام می‌برد، به‌صورت یک آرایه یا یک رشتهٔ جداشده با ویرگول، و هر دو status: (delivered یا failed)، since: و until: را می‌گیرند، یعنی فیلترهای زبانهٔ «تحویل‌ها» در کنسول. listActivity و listWorkspaceActivity گزارش ممیزی را می‌خوانند: چه کسی چه چیزی را ساخت، تغییر داد، روشن یا خاموش کرد، چرخاند، آزمود، بازپخش کرد یا حذف کرد. هرکدام یک نسخهٔ listAll و یک نسخهٔ iterate در کنار خود دارند، مانند listAllDeliveries و iterateDeliveries، و هر ردیف گزارش فضای کاری endpointId دارد. webhooks->stats عددهای پشت زبانهٔ «تحلیل» را برای بازه‌ای که انتخاب می‌کنید برمی‌گرداند.

since: و until: یک DateTimeInterface یا یک رشته با قالب ISO 8601 می‌گیرند، و رشته‌ای که فقط یک تاریخ دارد یعنی نیمه‌شب UTC همان روز. until: باید دیرتر از since: باشد، وگرنه فراخوانی یک InvalidRequestException پرتاب می‌کند که errorCode آن برابر invalid_parameter است.