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