Mailfornet

Webhooks

Instead of asking whether mail has arrived, get told. We POST to your endpoint the moment a message reaches any of your API addresses.

When to use them

For a test that is waiting on one email, ?wait= or /code is simpler, because it's a single request that returns when the mail lands. Webhooks suit everything else: a service that processes inbound mail, a queue worker, a dashboard, or many inboxes at once.

1. Register an endpoint

curl -X POST https://api.mailfornet.com/v1/webhooks \
  -H "Authorization: Bearer mf_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://yourapp.com/hooks/mailfornet"}'

{"id": 12, "url": "https://yourapp.com/hooks/mailfornet", "secret": "whsec_…", "createdAt": "…"}

The secret is shown once. Store it with your other secrets. You can also create and manage endpoints in your account under Webhooks.

2. What we send

POST /hooks/mailfornet
content-type: application/json
user-agent: Mailfornet-Webhook/1
mailfornet-event: message.received
mailfornet-timestamp: 1790812800
mailfornet-signature: 5f2b…c91e

{
  "event": "message.received",
  "createdAt": "2026-09-25T17:12:06.838Z",
  "data": {
    "address": "ryan.grant688@tikaurasave.site",
    "from": "no-reply@github.com",
    "subject": "Your GitHub launch code",
    "intro": "Enter code 731905 to finish…",
    "receivedAt": "2026-09-25T17:12:06.838Z"
  }
}

The payload is a notification, not the whole message. To get the body, the extracted code and the links, call GET /v1/inboxes/{address}/code or list the messages and read the one you need.

3. Verify the signature

The signature is a hex HMAC-SHA256 of ${timestamp}.${rawBody}, using your endpoint's secret. Check it against the raw body. If you parse the JSON and serialise it again, the bytes change and the signature won't match. Reject timestamps more than five minutes old, so a captured delivery can't be replayed later.

Node.js, with the official client:

import express from 'express';
import { verifyWebhook } from 'mailfornet';

const app = express();

app.post('/hooks/mailfornet', express.text({ type: '*/*' }), async (req, res) => {
  const ok = await verifyWebhook({
    secret: process.env.MAILFORNET_WEBHOOK_SECRET,
    body: req.body, // the raw string
    signature: req.get('mailfornet-signature'),
    timestamp: req.get('mailfornet-timestamp'),
  });
  if (!ok) return res.sendStatus(401);

  const { event, data } = JSON.parse(req.body);
  if (event === 'message.received') queue.add(data);
  res.sendStatus(200);
});

Python (Flask):

import hmac, hashlib, os, time
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = os.environ["MAILFORNET_WEBHOOK_SECRET"].encode()

@app.post("/hooks/mailfornet")
def mailfornet():
    ts = request.headers.get("mailfornet-timestamp", "")
    sig = request.headers.get("mailfornet-signature", "")
    raw = request.get_data()  # bytes, exactly as sent
    if not ts.isdigit() or abs(time.time() - int(ts)) > 300:
        abort(401)
    expected = hmac.new(SECRET, ts.encode() + b"." + raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, sig):
        abort(401)
    event = request.get_json()
    # … handle event["data"]
    return "", 200

4. Test without sending an email

curl -X POST https://api.mailfornet.com/v1/webhooks/12/test \
  -H "Authorization: Bearer mf_live_your_key"

This sends a signed webhook.test event to that one endpoint and returns the status your server answered with, so you can check your signature code before any real mail flows.

Delivery rules

Next

API reference · Code examples · Quickstart