Features

Webhooks

Signed HTTP callbacks with retries, delivery logs and replay.

Events

EventWhen
email.receivedA message was stored and processed (labels included)
email.labeledLabels were applied (rules or Clef)
inbox.createdAn inbox was created (API, dashboard, MCP or catch-all)
inbox.expiredA disposable inbox reached its expiry
webhook.testYou pressed Send test

Endpoints can be filtered by inbox ids and by label keys — e.g. only email.labeled for urgent.

Payload

{
	"id": "dlv_…",
	"type": "email.received",
	"created_at": "2026-10-07T15:14:24.000Z",
	"data": {
		"inbox": { "id": "inb_…", "address": "swift-otter-4821@acme-agents.com" },
		"email": {
			"id": "eml_…",
			"from": { "address": "no-reply@acme.io", "name": "Acme" },
			"subject": "Your Acme verification code",
			"snippet": "Your verification code is 482913…",
			"labels": [{ "key": "verification", "source": "rule", "confidence": 1 }],
			"verification": { "code": "482913", "link": "https://acme.io/verify?token=abc" },
			"received_at": "2026-10-07T15:14:24.000Z"
		}
	}
}

Fetch the full message with GET /api/v1/emails/{id}.

Headers

HeaderValue
Squadmail-Eventevent type
Squadmail-Deliverydelivery id (use it for idempotency)
Squadmail-Signaturet=UNIX_SECONDS,v1=HEX_HMAC

Verifying signatures

The signature is HMAC_SHA256(secret, t + "." + raw_body).

import crypto from 'node:crypto';

export function verify(rawBody: string, header: string, secret: string) {
	const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
	const expected = crypto
		.createHmac('sha256', secret)
		.update(`${parts.t}.${rawBody}`)
		.digest('hex');
	const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
	return fresh && crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}

Retries

Any non-2xx response or a timeout (10 s) is retried via Cloudflare Queues with exponential backoff — about 10 s, 40 s, 3 min, 11 min, 43 min. After that the delivery is marked failed. Every attempt is visible in the delivery log, and any delivery can be replayed.

Edit this page on GitHub