Webhooks
Werkmail POSTs a JSON envelope to your HTTPS URL when message or suppression events occur. URLs must be public HTTPS (SSRF-guarded). Secrets are shown once (whsec_…).
Create a subscription
curl -sS -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "ops",
"url": "https://example.com/hooks/werkmail",
"events": [
"message.delivered",
"message.hard_bounced",
"message.spam_complaint",
"message.failed",
"message.content_blocked",
"suppression.added"
]
}' \
"$BASE/api/practices/$PRACTICE_ID/webhooks"
List, delete, test, and inspect deliveries:
GET /api/practices/{id}/webhooksDELETE /api/practices/{id}/webhooks/{webhookID}POST /api/practices/{id}/webhooks/{webhookID}/testGET /api/practices/{id}/webhooks/{webhookID}/deliveries
Envelope
{
"id": "delivery-uuid",
"event": "message.delivered",
"timestamp": "2026-08-21T12:00:00Z",
"data": {
"message_id": "…",
"status": "delivered"
}
}
Headers
| Header | Meaning |
|---|---|
Content-Type |
application/json |
X-Werkmail-Event |
Event name |
X-Werkmail-Timestamp |
Unix seconds |
X-Werkmail-Signature |
Hex HMAC-SHA256 of {timestamp}.{body} using the webhook secret |
User-Agent |
werkmail-webhooks/1.0 |
Verify in constant time. Reject timestamps older than a few minutes to stop replays.
import hmac, hashlib
def valid(secret: str, timestamp: str, body: bytes, signature_hex: str) -> bool:
mac = hmac.new(secret.encode(), f"{timestamp}.".encode() + body, hashlib.sha256)
return hmac.compare_digest(mac.hexdigest(), signature_hex.lower())
Event names
Message: message.created, message.sent, message.delivered, message.opened, message.clicked, message.hard_bounced, message.soft_bounced, message.auto_replied, message.spam_complaint, message.failed, message.content_blocked, message.suppressed, message.unsubscribed, message.delivery_delayed, message.subscription.
Suppression: suppression.added, suppression.removed (include scope, project_id, route_id, and slugs when known).
Test: webhook.test.
Open, click, and delay events require open/click tracking on the route or send settings.
Slack / Teams alerts
POST /api/practices/{id}/alerts with kind slack or teams and a public incoming-webhook URL. Default events: hard bounce, soft bounce, complaint, content blocked, failed send.