Back to homeDocs

Documentation

Everything you need to receive an email as a signed HTTP request.

Your address

Every account gets one permanent address shaped like inbox_ab12cd34ef@tempy.uk. It cannot be changed, and it is the only address that reaches your webhook. Forward mail to it from any provider, or hand it out directly.

inbox_7wgaav5mdb@tempy.uk

The payload

One HTTPS POST per email, sent as application/json. The body carries the email id, from, to, subject, the text and HTML bodies, every original header and an array of attachments. A header that appeared once is a string; a header that was repeated is an array of strings, in the order the message carried them.

Payload
{
  "id": "em_wnrppxfrp5p9wvv1",
  "from": "you@gmail.com",
  "to": "inbox_7wgaav5mdb@tempy.uk",
  "subject": "Your verification code",
  "text": "Your code is 482913",
  "html": "<p>Your code is <b>482913</b></p>",
  "headers": {
    "message-id": "<…@mail.gmail.com>"
  },
  "attachments": [],
  "received_at": "2026-09-03T10:42:11Z"
}

Verifying the signature

Each request carries an X-Mailhook-Signature header: sha256= followed by the HMAC-SHA256 of the raw request body, keyed with your signing secret. Compute it over the bytes you received, before any JSON parsing, and compare in constant time. Reject anything that does not match.

X-Mailhook-Signature: sha256=8f1c4e9a…

Verify the signatureNode.jsPython

Rotating your secret

While a rotation is in progress the primary signature is still computed with your current secret, and the new one arrives in X-Mailhook-Signature-Next. X-Mailhook-Secret-Version names the version behind the primary header. Deploy the new secret, then activate it — nothing breaks in between.

X-Mailhook-SignatureX-Mailhook-Signature-NextX-Mailhook-Secret-Version

Retries and timeouts

Any 2xx counts as delivered. A 5xx, a 429 or a network failure is retried up to five times, roughly 30 seconds, 1 minute, 2 minutes and 4 minutes apart with jitter. A 4xx other than 429 is permanent and never retried. Your endpoint has 30 seconds to answer.

5×+30s+1m+2m+4m30s

Attachments

Files are not inlined in the JSON. Each attachment carries its filename, content type, size and a signed URL that stays valid for 24 hours from the moment the email arrived. Fetch what you need inside that window — afterwards the file is gone with the rest of the content.

filename · content_type · size · url · expires_at

Limits

A message may be up to 20 MB including attachments; anything larger is rejected at the door so the sender gets a bounce they can act on. Test requests and manual retries are rate limited per account, and a test request carries X-Mailhook-Test: true so you can tell it apart.

20 MB · X-Mailhook-Test: true

Idempotency

A retry sends the same body again with the same email id, so treat that id as your deduplication key. Two POSTs carrying one id are the same email, not two.

X-Mailhook-Id: em_wnrppxfrp5p9wvv1