nerfmail

reference

Security

Authentication, webhook verification, rate limits, and security model.

Authentication

Nerfmail uses two levels of authentication:

Admin Token

Account management endpoints (creating accounts, mailboxes, API keys, webhooks, etc.) require your admin token:

Authorization: Bearer {ADMIN_TOKEN}

Mailbox API Keys

All mailbox operations use API keys scoped to a single mailbox:

Authorization: Bearer nrfm_{keyId}.{secret}

API keys are issued once and cannot be retrieved again. The secret is hashed before storage — even a database breach won't expose your keys.

To revoke a compromised key:

bash
curl -X POST https://api.nerfmail.com/v1/mailboxes/{mailboxId}/api-keys/{keyId}/revoke \
  -H "Authorization: Bearer $ADMIN_TOKEN"

Webhook Signature Verification

Every webhook delivery includes an HMAC-SHA256 signature in the x-nerfmail-signature header. Always verify this to confirm the payload came from Nerfmail.

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");
}

Protocol callbacks use the same pattern with the x-nerfmail-callback-signature header.

Secure Reply Tokens

Outbound emails use cryptographic reply-to aliases so your real mailbox address structure isn't exposed to recipients:

Property Detail
Format {localPart}+reply-{token}@{domain}
Expiry 30 days from creation
Scope Bound to a specific mailbox and thread

Expired tokens return 410 Gone. Invalid tokens return 400 Bad Request.

Rate Limits

Scope Limit Window
Mailbox sends 1,000 (configurable) Per hour, sliding window
API key calls 600 Per 10 minutes, sliding window

Exceeding a rate limit returns 429 Too Many Requests.

You can adjust the mailbox send limit through the mailbox policy endpoint.

Suppression List

Outbound sends automatically check the account-level suppression list. Attempts to send to a suppressed address return 409 Conflict with code address_suppressed.

Mailbox Policies

Fine-grained per-mailbox controls:

Policy Default Effect
allow_outbound true Blocks send/reply if false
allow_auto_reply true Blocks agent protocol auto-replies if false
max_outbound_per_day 1000 Send rate limit per hour

Input Validation

All request bodies are validated before processing:

  • Email addresses are format-checked
  • Slugs must be lowercase alphanumeric with hyphens
  • Action parameters and response schemas are validated against their JSON Schema definitions
  • Protocol messages enforce consistency rules (action/subject/parameters)
  • Query parameters are type-checked and range-validated