API Keys & Webhook Telemetry
Generate em_live_ API credentials and subscribe to cryptographically signed HMAC-SHA256 delivery webhooks.
EmitMail provides API keys and a webhook dispatcher that retries failed deliveries according to its queue configuration.
1. Generating API Keys
Navigate to Dashboard -> API Keys and click + Create API Key.
Assign a descriptive label (e.g., Production Backend API) and choose whether the key is scoped to a single verified domain or has multi-tenant workspace permissions.
Copy your secret API token immediately. Tokens use the standard format:
em_live_9f81a7b4892c48e89b33a5cf982e01b42. Webhook Event Subscriptions
Navigate to Dashboard -> Webhooks to register your server's callback URL. Supported events include:
| Event Identifier | Trigger Condition |
|---|---|
email.delivered | Remote MTA accepted message (250 OK status) |
email.bounced | Remote MTA rejected message (550 HardFail or Mailbox Unavailable) |
email.complaint | Recipient marked email as spam (Feedback Loop ARF report) |
email.suppressed | Outbound dispatch blocked by recipient suppression list |
email.unsubscribed | User clicked one-click List-Unsubscribe link |
inbound.email.received | Incoming email ingested and parsed by the /inbox engine |
3. Verifying Webhook Signatures (HMAC-SHA256)
Webhook requests include these verification headers:
X-EmitMail-Signature:sha256=<hex_hmac_signature>X-EmitMail-Event: Event name (e.g.email.delivered)X-EmitMail-Delivery: Unique delivery attempt UUID
Node.js / Express Signature Verification Example
import crypto from 'node:crypto';
import express, { Request, Response } from 'express';
const app = express();
// Ensure you capture the raw body buffer for HMAC calculation
app.use(express.json({
verify: (req: any, _res, buf) => {
req.rawBody = buf;
}
}));
app.post('/api/emitmail-webhook', (req: Request, res: Response) => {
const signature = req.headers['x-emitmail-signature'];
const webhookSecret = process.env.EMITMAIL_WEBHOOK_SECRET;
const rawBody = (req as any).rawBody as Buffer | undefined;
if (typeof signature !== 'string' || !webhookSecret || !rawBody) {
return res.status(401).json({ error: 'Missing webhook verification data' });
}
const expectedSignature = `sha256=${crypto
.createHmac('sha256', webhookSecret)
.update(rawBody)
.digest('hex')}`;
const received = Buffer.from(signature);
const expected = Buffer.from(expectedSignature);
if (received.length !== expected.length || !crypto.timingSafeEqual(received, expected)) {
return res.status(401).json({ error: 'Invalid webhook signature' });
}
const { event, data } = req.body;
console.log(`Received verified event [${event}]:`, data);
res.status(200).json({ received: true });
});