Skip to the documentation
SDK

Endpoints

`webhooks.list`, `get`, `create`, `update`, `delete`, `rotateSecret`, `test` and `listDeliveries`.

Every method

usage.ts
const endpoint = await openemail.webhooks.create({  url: 'https://acme.com/hooks/mail',  eventTypes: ['email.sent', 'email.bounced'],  description: 'Billing service',}) await store(endpoint.secret) await openemail.webhooks.list()await openemail.webhooks.get(endpoint.id)await openemail.webhooks.update(endpoint.id, { enabled: false })await openemail.webhooks.test(endpoint.id)const rotated = await openemail.webhooks.rotateSecret(endpoint.id)await openemail.webhooks.delete(endpoint.id)

create is the ONLY time the secret is returned, apart from rotateSecret. A read never echoes it, so store it before doing anything else. Omit eventTypes to receive every event, later ones included.

rotateSecret has no overlap window. The old secret stops working immediately, so deploy the new one before you rotate. It is never retried automatically: a retry would rotate a second time and invalidate the secret the first attempt returned.

What you can subscribe to

WEBHOOK_EVENTS is exported so you can render the list. Events are events of the **mailbox**, not of this API: email.received fires for mail that arrives in the app, and email.sent fires for a message the composer sent. Subscribing is not the same as watching your own API traffic.

Proving it works

webhook-test.ts
const result = await openemail.webhooks.test('whe_…')console.log(result.delivery?.status, result.delivery?.responseCode) const deliveries = await openemail.webhooks.listDeliveries('whe_…')for (const d of deliveries) console.log(d.eventType, d.status, d.responseCode, d.error)

A responseCode of null means there was no response at all (DNS, TLS, a timeout), which is a different fact from a response that said 0. Each row carries attempt and maxAttempts, so several rows can describe one event: the same payload.id across them is the event, and the attempt number is the try.

Parameters: webhooks.create

urlstringrequired
Where deliveries are POSTed. HTTPS only, and the host may not be `localhost`, a `.localhost`/`.local`/`.internal` name, or a loopback, private, CGNAT or link-local IP literal. This is a server-side fetch to an address you supply, so those are a 422 on `url`; the check reads the hostname as written and never resolves DNS. What is stored is the URL parser's serialisation of what you sent, so `https://acme.com` reads back as `https://acme.com/`.
eventTypesWebhookEvent[]
Which events reach this endpoint: any of the names in `WEBHOOK_EVENTS`. `POST /webhooks` caps the array at the number of events that exist, so one more than that is a 422 on `eventTypes`; `PATCH` does not cap it. Only the length is capped, and a repeated name is stored and read back exactly as you sent it. Omitted or empty is stored as an empty list, which is why it reads back as `['*']`, and it means every `email.*` event except `email.replied`, fourteen today, and never the domain or suppression families. A family added later never reaches an endpoint that did not name it, so an integration cannot start receiving a shape it has never seen because of a release.
descriptionstring
A label for the endpoint, at most 200 characters, so a list of webhooks reads as names rather than a column of URLs. Omitted, it is stored and returned as null.

Response: CreatedWebhookResource

object'webhook'
Always `'webhook'`, the same discriminator a plain read returns, because the secret is one extra key on the ordinary shape rather than an object type of its own. Whether `secret` is present is decided by which method you called, not by this field.
idstring
The endpoint's identifier: `whe_` followed by 24 hex characters. Every other webhook call takes it: `get`, `update`, `delete`, `rotateSecret`, `test` and `listDeliveries`.
urlstring
The endpoint as stored, having passed the HTTPS and blocked-host checks. It is the parsed URL re-serialised, so compare against this value rather than against the string you sent.
descriptionstring | null
The label you gave it, or null if you gave none. An `update` that sends an explicit null clears it back to null.
eventTypesWebhookEvent[] | ['*']
The subscribed events, or `['*']` when the endpoint named none. `['*']` is how an empty stored list is rendered on read and cannot be sent back, and it stands for the thirteen message events rather than the whole catalogue. `create` and `update` accept only the literal event names.
enabledboolean
Whether deliveries are attempted; a disabled endpoint is skipped when events are dispatched and keeps its secret and its delivery history. Always true here, since `WebhookCreate` has no `enabled` and only `WebhookPatch` does.
lastDeliveryAtstring | null
ISO 8601 timestamp of the last delivery ATTEMPT, not the last success. It is stamped after a failed POST too, so it tells you the endpoint was tried and `listDeliveries` tells you how it went. Null until the first attempt, and so always null on `create`.
createdAtstring
ISO 8601 timestamp of when the endpoint was registered. `list` returns endpoints newest first by this field.
secretstring
The HMAC-SHA-256 key that signs each delivery's `X-OpenEmail-Signature`: `whsec_` followed by 32 random bytes in base64url, and what you hand to `verifyWebhookSignature`. Returned by `create` and `rotateSecret` and by nothing else. A read never echoes it, so store it now; a lost secret can only be replaced with `rotateSecret`, which invalidates the old one immediately.