Maildesk Webhooks — Integration Guide#
Maildesk webhooks let your system receive real-time HTTP callbacks when subscriber/contact events happen in your Maildesk account.
1) Overview#
When an event occurs, Maildesk will:Send an HTTP POST request to your configured Webhook Endpoint URL
Include an event payload in the request body (JSON)
Retry delivery with backoff if your endpoint doesn’t respond successfully
2) Supported Event Types#
You can subscribe to one or more of these event types:| Event type | Meaning |
|---|
subscriber.created | A subscriber/contact was created |
subscriber.updated | A subscriber/contact was updated |
subscriber.confirmed | A subscriber/contact confirmed (e.g., confirmed opt-in) |
subscriber.unsubscribed | A subscriber/contact unsubscribed |
3) Delivery: HTTP Request#
Method#
Endpoint#
Your configured webhook URL, e.g. https://example.com/webhooks/maildesk
Maildesk sends a standard JSON POST. (Any custom authentication/signature headers are not specified here—see Security below.)Body (JSON)#
Maildesk posts a JSON object representing the webhook event.At minimum, you should expect:type — the event type string
eventId — a unique ID for this event (use for idempotency)
4) Payload Schema#
Base payload#
{
"type": "subscriber.updated",
"eventId": "01HZYX...ULID_OR_OTHER_ID"
}
For subscriber-related events, Maildesk may include contact fields like:{
"type": "subscriber.created",
"eventId": "01HZYX...ULID_OR_OTHER_ID",
"id": "contact_id",
"firstName": "Jane",
"lastName": "Doe",
"email": "jane.doe@example.com",
"status": "active",
"createdAt": "2026-01-12T10:20:30.000Z"
}
eventId is designed specifically to help you implement idempotency.
createdAt is a timestamp (treat as ISO-8601 when you receive it; some integrations may deliver as a date string).
status is a string (exact set of values depends on your subscriber model).
5) Success / Failure Handling#
Maildesk considers the webhook delivery successful if your endpoint responds with any of:If you return other status codes (or time out / refuse connection), Maildesk treats it as a failure and will retry.Recommendation#
Return 200 OK as quickly as possible after basic validation, and process the event asynchronously in your system.
6) Retries & Backoff#
If delivery fails, Maildesk retries the webhook with increasing delays.After the retries are exhausted, the event is moved to a dead-letter flow (i.e., it will no longer be retried automatically).Important: Because retries happen, you must implement idempotency using eventId.
7) Idempotency (Required)#
Since the same event can be delivered more than once (retries, network issues), treat webhooks as at-least-once delivery.What to do#
Store each eventId you’ve processed (database table, cache with persistence, etc.).
If you receive the same eventId again, do nothing (or safely re-run if your processing is naturally idempotent).
8) Security Recommendations#
From the delivery behavior shown, the request is a plain POST to your endpoint.Because of that, you should protect your endpoint with one (or more) of:A secret in the URL (e.g. https://example.com/webhooks/maildesk/<secret>)
Basic Auth (username/password)
IP allowlisting (if Maildesk provides stable IPs—otherwise not recommended)
HMAC signature verification (ideal; requires Maildesk to send a signature header)
If you want, tell me which security approach you prefer, and I can draft a “Security” section that matches it (and suggest the exact request validation logic).
9) Implementation Example (Receiver)#
Minimal Express/Nest-compatible handler (Node.js)#
import type { Request, Response } from "express";
export function maildeskWebhookHandler(req: Request, res: Response) {
const event = req.body as { type: string; eventId: string };
// 1) Validate required fields
if (!event?.type || !event?.eventId) {
return res.status(400).json({ error: "Missing type or eventId" });
}
// 2) Idempotency check (pseudo-code)
// if (await alreadyProcessed(event.eventId)) return res.sendStatus(200);
// 3) Enqueue for async processing (recommended)
// await enqueue(event);
// 4) Acknowledge quickly
return res.sendStatus(200);
}
10) Troubleshooting Checklist#
Your endpoint must be publicly reachable over the internet.
Ensure your endpoint returns 200/201/202 promptly.
Handle retries safely using eventId.
Log incoming payloads + response codes to diagnose issues.
Modified at 2026-01-12 06:26:23