Skip to main content

Webhooks

Receive real-time HTTP notifications when events occur in your account.

Overview

Webhooks allow your application to receive real-time POST requests whenever specific events happen in your mailservr account, such as receiving an email or creating a new thread.

Webhooks are available on the Enterprise plan. You can register up to 5 webhook endpoints from Settings.

Events

email.received

Fired when an inbound email is received by one of your aliases.

{
  "event": "email.received",
  "data": {
    "emailId": 142,
    "threadId": 87,
    "from": {
      "address": "[email protected]",
      "name": "Jane Doe"
    },
    "to": ["[email protected]"],
    "subject": "Meeting tomorrow",
    "snippet": "Hey, are we still on for...",
    "receivedAt": "2026-02-13T14:30:00.000Z"
  }
}

email.sent

Fired when you send an email (compose or reply).

{
  "event": "email.sent",
  "data": {
    "emailId": 143,
    "threadId": 87,
    "from": "[email protected]",
    "to": ["[email protected]"],
    "subject": "Re: Meeting tomorrow",
    "sentAt": "2026-02-13T15:00:00.000Z"
  }
}

thread.created

Fired when a new email thread is created (from an inbound email or a new compose).

{
  "event": "thread.created",
  "data": {
    "threadId": 87,
    "subject": "Meeting tomorrow",
    "addressId": 12,
    "createdAt": "2026-02-13T14:30:00.000Z"
  }
}

Request Format

Each webhook delivery is an HTTPS POST request with a JSON body. The following headers are included:

HeaderDescription
Content-TypeAlways application/json
X-Webhook-SignatureHMAC-SHA256 hex digest for verifying authenticity
X-Webhook-TimestampUnix timestamp (seconds) when the request was signed
User-Agentmailservr-webhooks/1.0

Verifying Signatures

Every webhook request is signed with the secret shown when you created the endpoint. You should always verify the signature to ensure the request is authentic and hasn't been tampered with.

Algorithm

  1. Extract the X-Webhook-Timestamp and X-Webhook-Signature headers
  2. Concatenate the timestamp, a period, and the raw request body: `${timestamp}.${body}`
  3. Compute HMAC-SHA256 of that string using your signing secret
  4. Compare the hex digest to the signature header (use a timing-safe comparison)
  5. Optionally reject requests where the timestamp is older than 5 minutes to prevent replay attacks

Node.js

import { createHmac, timingSafeEqual } from "crypto";

function verifyWebhook(body, signature, timestamp, secret) {
  const signedPayload = `${timestamp}.${body}`;
  const expected = createHmac("sha256", secret)
    .update(signedPayload)
    .digest("hex");

  const sigBuf = Buffer.from(signature, "utf8");
  const expBuf = Buffer.from(expected, "utf8");

  if (sigBuf.length !== expBuf.length) return false;
  return timingSafeEqual(sigBuf, expBuf);
}

Python

import hmac
import hashlib

def verify_webhook(body: str, signature: str, timestamp: str, secret: str) -> bool:
    signed_payload = f"{timestamp}.{body}"
    expected = hmac.new(
        secret.encode(), signed_payload.encode(), hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Go

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
)

func VerifyWebhook(body, signature, timestamp, secret string) bool {
    signedPayload := fmt.Sprintf("%s.%s", timestamp, body)
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(signedPayload))
    expected := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(signature))
}

Best Practices

  • Always verify signatures. Never trust a webhook request without checking the HMAC signature against your signing secret.
  • Respond quickly. Return a 2xx status code within 10 seconds. Do heavy processing asynchronously after responding.
  • Use HTTPS. Webhook endpoints must use HTTPS. HTTP URLs are rejected.
  • Handle duplicates. In rare cases the same event may be delivered more than once. Use the emailId or threadId to deduplicate.
  • Check the timestamp. Reject requests where the X-Webhook-Timestamp is older than 5 minutes to prevent replay attacks.
  • Keep your secret safe. The signing secret is shown only once when you create the endpoint. Store it securely. If compromised, delete the endpoint and create a new one.

Delivery Behavior

  • Deliveries time out after 10 seconds.
  • A delivery is considered successful if your endpoint returns a 2xx status code.
  • Failed deliveries are logged and visible in your settings page. There are no automatic retries.
  • Delivery logs are retained for 30 days.
  • You can toggle endpoints on/off without deleting them.

Managing Webhooks

Webhook endpoints are managed from the Webhooks section in your Settings page. You can also manage them via the API:

MethodEndpointDescription
GET/api/webhooksList your webhook endpoints
POST/api/webhooksCreate a new endpoint
PATCH/api/webhooks/:idUpdate events or toggle enabled
DELETE/api/webhooks/:idDelete an endpoint
GET/api/webhooks/:id/deliveriesView delivery logs

All endpoints require authentication via session cookie or Authorization: Bearer <api-key> header.