Skip to main content

Webhooks

MethodPathScopeSDK
GET/api/v1/webhooksany valid keywebhooks.list
POST/api/v1/webhookswebhooks:writewebhooks.create
PATCH/api/v1/webhooks/:idwebhooks:writewebhooks.update
DELETE/api/v1/webhooks/:idwebhooks:writewebhooks.delete
GET/api/v1/webhooks/:id/deliveriesany valid keywebhooks.deliveries
POST/api/v1/webhooks/:id/rotatewebhooks:writewebhooks.rotate
POST/api/v1/webhooks/:id/pingwebhooks:writewebhooks.ping

For verifying what Mailroom sends to you, see the webhooks guide.

POST /api/v1/webhooks

FieldTypeNotes
urlstringRequired. Public HTTPS URL. Validated against SSRF — private, loopback, and link-local addresses are refused, and redirects are never followed.
eventsstring[]Subscribe to a subset. Omit or empty = every event. Must be known event types.
secretstringBring your own signing secret; otherwise one is generated.

201

{
"ok": true,
"endpoint": {
"id": "wh_7a2c…",
"url": "https://app.example.com/hooks/mailroom",
"events": ["email.bounced", "contact.unsubscribed"],
"enabled": true,
"created_at": "2026-07-28T10:00:00.000Z"
},
"secret": "whsec_9f2a…"
}
The secret is returned once

secret appears only in this response (and in rotate). It cannot be read back — store it before you discard the response.

Subscribable event types

contact.subscribed, contact.unsubscribed, email.sent, email.delivered, email.opened, email.clicked, email.bounced, email.complained, sequence.completed, campaign.completed.

An unknown name in events is a 422. Note that webhook.ping is not subscribable — see below.

GET /api/v1/webhooks

{ "ok": true, "webhooks": [{ "id": "wh_7a2c…", "url": "…", "events": null, "enabled": true, "created_at": "…" }] }

events: null means all events. Secrets are never returned.

PATCH /api/v1/webhooks/:id

Body: any of { "url", "events", "enabled" }.

{ "ok": true, "endpoint": { "id": "wh_7a2c…", "enabled": false,} }

Disabling with enabled: false stops delivery without losing the endpoint or its secret — the reasonable first move when your receiver is broken.

DELETE /api/v1/webhooks/:id

{ "ok": true, "id": "wh_7a2c…", "deleted": true }

Queued deliveries for the endpoint are dropped.

GET /api/v1/webhooks/:id/deliveries

Query: limit (rows, newest first).

{
"ok": true,
"deliveries": [
{
"id": "d_51b0…",
"event_type": "email.bounced",
"payload": { "email": "bounced@example.com", "template": "welcome" },
"status": "failed",
"attempts": 3,
"response_status": 500,
"next_attempt_at": "2026-07-28T10:24:00.000Z",
"created_at": "2026-07-28T10:08:00.000Z"
}
]
}

status: pending, delivered, failed. Deliveries are drained about once a minute; a non-2xx response is retried with exponential backoff up to 6 attempts (2, 4, 8 … minutes, capped at 60), then left failed. This log is where you look first when your receiver "never got the event".

POST /api/v1/webhooks/:id/rotate

Mints a new signing secret and returns it once. The old secret stops working immediately, so deploy the new one before rotating (or accept a gap).

{ "ok": true, "endpoint": { "id": "wh_7a2c…",}, "secret": "whsec_new…" }

POST /api/v1/webhooks/:id/ping

Sends a signed webhook.ping event inline and reports what your receiver said. Not persisted to the delivery log, and webhook.ping is deliberately not a subscribable type — so a ping never reaches production handlers that filter on subscribed events.

{ "ok": true, "delivered": true, "responseStatus": 204 }

delivered: false with responseStatus: null means the request never completed (DNS, TLS, timeout, or the SSRF gate refused the address).

404 not_found on any of these endpoints means no such webhook in this project.