Webhooks

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

  1. Expose a public HTTPS URL that accepts POST requests and returns a 2xx response within 10 seconds.
  2. Open Dashboard → Developer, enter the URL and select Add.
  3. 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

  1. Read the raw request bytes before JSON parsing.
  2. Build the signed value as <timestamp>.<raw-body>.
  3. Compute HMAC-SHA256 with the mbwh_… secret and encode the digest as lowercase hexadecimal.
  4. 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.