nerfmail

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

bash
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:

json
{
  "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:

json
{
  "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:

json
{
  "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:

typescript
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:

bash
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:

bash
curl -X POST https://api.nerfmail.com/v1/webhooks/{endpointId}/deliveries/{deliveryId}/replay \
  -H "Authorization: Bearer $ADMIN_TOKEN"

List Webhooks

bash
curl https://api.nerfmail.com/v1/mailboxes/{mailboxId}/webhooks \
  -H "Authorization: Bearer $ADMIN_TOKEN"