guides
Webhooks
Get real-time notifications when email events happen in your mailboxes.
Webhooks let you react to events in real time — a new message arrives, a reply is sent, an agent responds. Nerfmail delivers signed payloads to your endpoint with automatic retries.
Create a Webhook
curl -X POST https://api.nerfmail.com/v1/mailboxes/{mailboxId}/webhooks \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: webhook-001" \
-d '{
"url": "https://your-app.example/hooks/nerfmail",
"events": ["message.received", "message.processed", "message.sent"]
}'Response:
{
"webhook": {
"id": "uuid",
"url": "https://your-app.example/hooks/nerfmail",
"events": ["message.received", "message.processed", "message.sent"],
"status": "active"
},
"signing_secret": "hex-encoded-secret"
}The signing_secret is shown once. Store it to verify webhook signatures.
Events
| Event | When it fires |
|---|---|
message.received |
An inbound email or agent protocol message arrives |
message.processed |
An inbound email has been parsed, assessed, and enriched |
message.sent |
An outbound email was successfully delivered |
protocol.response.created |
A response was created for an agent protocol message |
Payload Format
Each webhook delivery is a POST request with these headers:
| Header | Description |
|---|---|
x-nerfmail-event |
Event name (e.g., message.processed) |
x-nerfmail-signature |
HMAC-SHA256 hex signature of the request body |
Content-Type |
application/json |
Message Events
For message.received, message.processed, and message.sent:
{
"id": "uuid",
"mailbox_id": "uuid",
"thread_id": "uuid",
"direction": "inbound",
"from_address": "sender@example.com",
"to_address": "acme@nerfmail.com",
"subject": "Hello",
"status": "processed",
"risk_level": "low",
"triage_label": "routine",
"text_body": "Message content...",
"html_body": "<p>Message content...</p>",
"attachments": [],
"suspicious_signals": []
}Protocol Response Events
For protocol.response.created:
{
"id": "uuid",
"protocol_message_id": "uuid",
"body": "Response text",
"structured_data": {},
"delivery_mode": "poll",
"status": "delivered"
}Verifying Signatures
Always verify the signature to confirm the payload came from Nerfmail. Compute HMAC-SHA256 of the raw request body using your signing secret:
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(signingSecret),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(rawBody));
const expected = Array.from(new Uint8Array(signature))
.map(b => b.toString(16).padStart(2, "0"))
.join("");
if (expected !== request.headers.get("x-nerfmail-signature")) {
throw new Error("Invalid signature");
}Retries
Failed deliveries (non-2xx responses or network errors) are retried with backoff:
| Attempt | Delay |
|---|---|
| 1 | 10 seconds |
| 2 | 20 seconds |
| 3 | 30 seconds |
| 4 | 40 seconds |
| 5 | 60 seconds |
After 5 failed attempts, the endpoint status moves to degraded. Successful delivery after a degraded state restores it to active.
Delivery History
Every delivery attempt is recorded. View the history:
curl "https://api.nerfmail.com/v1/webhooks/{endpointId}/deliveries?limit=20" \
-H "Authorization: Bearer $ADMIN_TOKEN"Each entry includes status_code, response_body, duration_ms, and whether it succeeded.
Replay a Delivery
If a delivery failed, you can replay it:
curl -X POST https://api.nerfmail.com/v1/webhooks/{endpointId}/deliveries/{deliveryId}/replay \
-H "Authorization: Bearer $ADMIN_TOKEN"List Webhooks
curl https://api.nerfmail.com/v1/mailboxes/{mailboxId}/webhooks \
-H "Authorization: Bearer $ADMIN_TOKEN"