Agent skill
Mautic Webhooks Skill
Receive and verify Mautic webhooks. Use when setting up Mautic webhook handlers, debugging Webhook-Signature verification, or handling events like mautic.lead_post_save_new, mautic.form_on_submit, or mautic.email_on_open.
Install this skill
npx skills add hookdeck/webhook-skills --skill mautic-webhooks
When to Use This Skill
- How do I receive Mautic webhooks?
- How do I verify the Mautic
Webhook-Signatureheader? - Why does my Mautic webhook signature verification fail?
- How do I handle
mautic.lead_orpost_ save_ new mautic.form_on_submitevents? - How do I process batched events from Mautic's queue mode?
Verification (core)
Mautic computes a base64 HMAC-SHA256 of the raw request body, keyed with the webhook's secret as a raw string (not decoded, no prefix), and sends it in the Webhook-Signature header. Despite the header name this is not Standard Webhooks: there is no webhook-id or webhook-timestamp, and nothing but the body is signed.
Node:
const crypto = require('crypto');
function verify(rawBody, signatureHeader, secret) {
if (!signatureHeader) return false;
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('base64');
try {
return crypto.timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));
} catch {
return false; // different lengths
}
}
Python:
import base64, hashlib, hmac
def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
if not signature_header:
return False
expected = base64.b64encode(
hmac.new(secret.encode(), raw_body, hashlib.sha256).digest()
).decode()
return hmac.compare_digest(signature_header.encode(), expected.encode())
Raw body: read the body as bytes before any JSON parsing, and accept any content type (Express:
express.raw({ type: '*/*' })).For complete handlers with route wiring, event dispatch, and tests, see:
Payload Shape
The body is an object keyed by event type; each value is an array of events, each with a timestamp. In queue (background) mode one request can contain several event types and several events per type, so iterate over all keys and all items.
{
"mautic.lead_post_save_new": [
{ "contact": { "id": 1 }, "timestamp": "2026-01-01T12:00:00+00:00" }
]
}
Common Event Types
| Event type | Description |
|---|---|
mautic.lead_ | Contact identified (created) |
mautic.lead_ | Contact updated |
mautic.lead_post_delete | Contact deleted |
mautic.lead_ | Contact points changed |
mautic.lead_ | Contact channel subscription status changed |
mautic.lead_ | Contact added to or removed from a company |
mautic.company_post_save | Company created or updated |
mautic.company_ | Company deleted |
mautic.email_on_send | Email sent to a contact |
mautic.email_on_open | Email presumably opened |
mautic.form_on_submit | Form submitted |
mautic.page_on_hit | Landing page or tracked page visited |
mautic.sms_on_send | Text message sent to a contact |
mautic.lead_list_change | Contact added to or removed from a segment (in Mautic's sender, not its docs) |
For payload fields, see Mautic webhook events and payloads.
Environment Variables
MAUTIC_WEBHOOK_SECRET=your_webhook_secret # Secret shown on the webhook's edit page in Mautic
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 mautic --path /webhooks/mautic
Reference Materials
- references/overview.md - Mautic webhook concepts, events, payload structure
- references/setup.md - Create and test a webhook in Mautic
- references/verification.md - Signature verification details and gotchas