Webhooks and HMAC
Webhooks push incoming mail and unsubscribe events to an HTTPS endpoint. New keys should include
webhook:write to create, update, or delete registrations. Read access remains accepted for older
keys where the compatibility path is documented.
Register and verify
Section titled “Register and verify”-
Register an HTTPS URL with
register_webhookorPOST /v1/webhooks. Omit the inbox to cover the project or provide one owned inbox. The signing secret is returned once. -
Receive
message.receivedorunsubscribe.receiveddeliveries. Extrovert signs the raw request body and includes the event name inX-Extrovert-Event. -
Verify
X-Extrovert-Signaturebefore parsing or trusting the body. -
Acknowledge with a successful HTTP response. Failed deliveries use bounded retry and backoff.
Manage registrations with list_webhooks, get_webhook, update_webhook, and delete_webhook.
Secrets are redacted after creation. An inbox create request may also include webhook_url.
Signature format
Section titled “Signature format”X-Extrovert-Signature: t=1749751542,v1=3b2a…X-Extrovert-Event: message.receivedThe signature is the hexadecimal HMAC-SHA256 of "{t}.{raw_body}" using the webhook secret. Verify the
exact bytes received. Re-serializing parsed JSON changes the signed input.
import { verifyWebhookSignature } from "@extrovert.dev/sdk";
const payload = await request.text();const valid = await verifyWebhookSignature({ payload, signature: request.headers.get("x-extrovert-signature")!, secret: process.env.EXTROVERT_WEBHOOK_SECRET!,});Reject a valid signature whose timestamp is outside the replay window your service accepts. Five minutes is a common starting point, but choose a window that matches expected queueing and clock skew.
Event bodies
Section titled “Event bodies”message.received includes the canonical message object. Its text and html fields independently
represent source MIME alternatives. extracted_text and extracted_html are nullable, best-effort
derivatives. A missing format is not synthesized from the other.
unsubscribe.received reports a recorded opt-out through the same signed delivery path. Treat the
event as notification; the server-side suppression row is authoritative.
Choosing a receive method
Section titled “Choosing a receive method”| Method | Use it when |
|---|---|
| Webhook | A long-running service needs signed push delivery |
wait_for_email | One task needs the next matching message or code before a deadline |
| Message or thread list | A worker or interface can poll bounded pages |
| Event stream | A connected console or agent needs low-latency notification and can reconnect with a cursor |
Webhooks and streams reduce latency but do not replace durable state. Re-read the message, suppression, or review record after reconnecting or recovering from a failed handler.