Receive signed message webhooks
Webhooks deliver message metadata and detected OTPs to an HTTPS endpoint. Verify the signature against the exact raw request body before parsing JSON.
Create an endpoint
- Expose a public HTTPS URL that accepts POST requests and returns a 2xx response within 10 seconds.
- Open Dashboard → Developer, enter the URL and select Add.
- Save the mbwh_… signing secret immediately. It is shown once.
Note
Redirects are not followed. Use the final HTTPS endpoint URL, not a URL shortener or an HTTP-to-HTTPS redirect.
Request format
- Method
POST- Content-Type
application/json- User-Agent
MailBots-Webhook/1.0- Event header
x-mailbots-event: message.received- Timestamp header
x-mailbots-timestamp: Unix seconds- Signature header
x-mailbots-signature: v1=<hex digest>
{
"id": "msg_…",
"event": "message.received",
"mailboxId": "mbx_…",
"envelopeTo": "build-102@otp.example.com",
"from": "noreply@example.dev",
"subject": "Your verification code",
"otp": "847291",
"receivedAt": "2026-09-16T09:00:00.000Z",
"expiresAt": "2026-09-19T09:00:00.000Z"
}Verify the signature
- Read the raw request bytes before JSON parsing.
- Build the signed value as <timestamp>.<raw-body>.
- Compute HMAC-SHA256 with the mbwh_… secret and encode the digest as lowercase hexadecimal.
- Compare it to the v1 value using a timing-safe comparison and reject stale timestamps; five minutes is a reasonable replay window.
expected = hex(HMAC_SHA256(webhookSecret, timestamp + '.' + rawBody))
valid = timingSafeEqual(expected, signatureHeaderWithoutV1)Delivery and retries
Any 2xx response marks the delivery successful. Timeouts, network errors and non-2xx responses are retried after approximately 1 minute, 5 minutes, 30 minutes, 2 hours and 10 hours before the delivery is marked failed.
Note
Make handlers idempotent using the payload id. A retry may deliver the same event more than once.