/api/v2/webhooks and are scoped per-user (the API key owner) โ x-workspace-key has no effect on webhook resources.POST /webhooks/endpoints with your url and the enabled_events you want. Save the secret from the response โ it is only ever returned once.X-Zapmail-Signature header. Recompute the HMAC on your side and compare before trusting the payload โ see Verifying signatures below.enabled_events| Field | Description |
|---|---|
id | Unique event ID (evt_ + UUIDv7). Use this to de-duplicate โ delivery is at-least-once. |
type | One of the event types listed below. |
created | Unix timestamp (seconds) the event was generated. |
api_version | Envelope schema version, currently 2026-07-01. |
data | Event-specific payload โ shape varies per type, see Event catalog. |
| Header | Value |
|---|---|
Content-Type | application/json |
X-Zapmail-Signature | t=<unix_ts>,v1=<hex_hmac_sha256> โ see Verifying signatures |
User-Agent | Zapmail-Webhooks/1.0 |
GET /webhooks/events (returns value/label/description/category for each type) โ use it to populate an event picker instead of hardcoding the strings below./api/v2/webhooks and require the standard auth headers (x-auth-zapmail required).| Method | Path | Description |
|---|---|---|
| GET | /events | List all subscribable event types with labels/descriptions/categories. |
| POST | /endpoints | Create an endpoint: { url, enabled_events[] }. Response includes secret โ shown only this once. |
| GET | /endpoints | List all endpoints owned by the caller (never includes secret). |
| PATCH | /endpoints/:id | Update url / enabled_events / status (partial, all optional). Setting status: "active" resets the endpoint's consecutive-failure counter. |
X-Zapmail-Signature header in the form:X-Zapmail-Signature: t=1753100000,v1=5257a869e7bfbe86...t โ the unix timestamp (seconds) the request was signed atv1 โ an HMAC-SHA256 hex digest of ${t}.${raw_request_body}, keyed with your endpoint's secret (the whsec_... value returned once from POST /endpoints)v1 using a constant-time comparison. Reject the request if the signatures don't match, or if t is further from the current time than you're comfortable with (recommended: 5 minutes) to guard against replay.1m โ 5m โ 30m โ 2h โ 5h โ 10h โ 24h, for 8 attempts total including the first.status flips to disabled after 20 consecutive failed deliveries (across all events, tracked at the endpoint level), and the owner is notified by email. Re-enable it with PATCH /endpoints/:id (status: "active"), which also resets the failure counter.X-Zapmail-Signature on every request โ see Verifying signatures.2xx as soon as the payload is durably queued; do the actual processing asynchronously rather than inline in the request handler, since slow handlers count toward the 10s timeout.resend.