Guide to Mailparser Webhooks: Features and Best Practices
Mailparser webhooks send the data Mailparser extracts from incoming emails to your own systems: order numbers and line items from order confirmations, totals from invoices, contact details from lead notifications. If you forward emails into a Mailparser inbox and need the results in a database, CRM or API, an Advanced Webhook delivers them as soon as each email is parsed.
This guide covers how Mailparser's Advanced Webhooks are configured, how the body template works, how to authenticate requests when Mailparser doesn't sign them, the retry options, and the best practices for receiving parsed email data in production.
What are Mailparser webhooks?
Mailparser describes Advanced Webhooks as "your Swiss Army knife for connecting any HTTP API to Mailparser". You add one to an inbox, and "parsed data will automatically be sent to the target URL specified in the settings of your Advanced Webhook".
You control almost everything about the request: the HTTP verb, the content type, extra headers and the whole body, which is a Handlebars template filled with the results of your parsing rules. That means there is no Mailparser event type and no fixed payload. The fields your receiver sees are the names of your own parsing rules.
There is one trigger, an email being parsed. By default all the parsed data from one email goes into one request. For tabular data you can switch to one request per table row.
Mailparser does not sign webhook requests. To authenticate them, you add a secret header in the webhook's Additional Headers field.
Mailparser webhook features
| Feature | Details |
|---|---|
| Configuration | An Advanced Webhook integration on an inbox |
| Trigger | An email in the inbox is parsed |
| Requests per email | One by default, or one per table row |
| HTTP verb | POST by default, or GET, PUT or DELETE |
| Content-Type | Form Data or JSON |
| Body | A Handlebars template you write |
| Headers | Additional Headers, one Name: value per line |
| Delay | Optional Webhook Delay between calls |
| Signature | None |
| Event type and event ID | None |
| Retries | Failed Webhook Auto-Retry, up to 4 retries, on eligible plans |
| Failure log | Failed Webhooks tab, with every attempt, status code and response |
| Alerts | Email alerts on 4xx and 5xx responses |
Mailparser publishes no source IP list, and says it uses "multiple servers for creating and sending webhook requests", so don't rely on the source IP.
The body template
The HTTP Body Payload Template is where you define the request body. Mailparser says it "lets you create 100% customized body payloads tailored to your API". It uses Handlebars, and the most important expressions are "simple identifiers (e.g. {{MyParsingRule}}) which will later be replaced with the parsed data". You can also loop over lists, such as table rows.
A JSON template for an invoice inbox might look like this:
{
"invoice_number": "{{InvoiceNumber}}",
"total": "{{Total}}",
"customer_email": "{{CustomerEmail}}"
}
The names in braces are your parsing rules. Changing the Content-Type or the What data to send option resets the template to a new default, so set those first, then edit the template. Preview Payload shows the template rendered with the data from the last parsed document, which is the quickest way to check it.
Because you write the body, include what your receiver needs to route and deduplicate: a document type if several inboxes share an endpoint, and a value that is unique per email, such as an invoice or order number you parse.
Setting up Mailparser webhooks
- Generate a random secret, for example with
openssl rand -hex 32, and store it on your receiving server asMAILPARSER_.WEBHOOK_ SECRET - In your Mailparser inbox, add an Advanced Webhook integration.
- Give it a name, leave HTTP Verb as
POST, enter your Target URL and set Content-Type to JSON. - Choose What data to send: one request per email, or one request per row for tabular data.
- In Additional Headers, add your secret on its own line, for example
X-Webhook-Secret: <your secret>. Mailparser's own example uses anAuthorization: Basic ...header, which works just as well. - Edit the body template, use Preview Payload to check it, and save.
- Optionally, turn on Failed Webhook Auto-Retry and failure alerts in the inbox settings.
For local development, the Hookdeck CLI (hookdeck listen 3000 mailparser --path /webhooks/mailparser) gives you a public HTTPS URL that forwards to your local server, with no account required. Use it as the Target URL and re-send a parsed email to test.
Securing Mailparser webhooks
With no signature, authentication means comparing the header you added with the value you stored, in constant time, and rejecting the request if it is missing or wrong. Compare the whole value, including any scheme such as Basic . Fail closed if the secret is not configured on your server, and serve the endpoint over HTTPS only.
const crypto = require('crypto');
const express = require('express');
function verifyMailparserHeader(headerValue, secret) {
if (!secret || typeof headerValue !== 'string') return false; // fail closed
const received = Buffer.from(headerValue, 'utf8');
const expected = Buffer.from(secret, 'utf8');
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}
const app = express();
app.post('/webhooks/mailparser', express.raw({ type: '*/*' }), (req, res) => {
if (!verifyMailparserHeader(req.get('x-webhook-secret'), process.env.MAILPARSER_WEBHOOK_SECRET)) {
return res.sendStatus(401);
}
const body = req.body.toString('utf8');
const data = req.is('application/json')
? JSON.parse(body)
: Object.fromEntries(new URLSearchParams(body)); // Form Data webhooks
// Store the parsed data, then answer with a 2xx.
res.sendStatus(200);
});
The same check in Python:
import hmac
import os
def verify_mailparser_header(header_value, secret) -> bool:
if not secret or not isinstance(header_value, str):
return False # fail closed
return hmac.compare_digest(header_value.encode("utf-8"), secret.encode("utf-8"))
# In FastAPI:
# verify_mailparser_header(request.headers.get("x-webhook-secret"),
# os.environ.get("MAILPARSER_WEBHOOK_SECRET"))
A static header proves the sender knows the secret. It doesn't protect the body or stop a captured request being replayed, so use HTTPS.
Make Mailparser webhooks production-ready. Hookdeck Event Gateway checks your secret header on every request, stops requests that fail before they reach your service, and durably queues the parsed data.
Mailparser webhook limitations and pain points
No signature
The Problem: Anyone who finds your Target URL can post data that looks like a parsed email.
Why It Happens: Mailparser's webhook options include no signature or signing secret. Additional Headers is the only way to add a credential.
Workarounds:
- Add a long random secret header to every Advanced Webhook and check it on every request.
- Use HTTPS so the header can't be read in transit.
- Treat the parsed data as untrusted input and validate it, since it comes from emails anyone can send to your inbox.
How Hookdeck Can Help: Hookdeck's Mailparser source asks for the header name and its exact value, and stops requests that fail before they reach your service.
Retries depend on your plan
The Problem: Your endpoint was down and the parsed data from several emails never arrived.
Why It Happens: Automatic retries are a setting, Failed Webhook Auto-Retry, with a maximum of 4 retries. Mailparser includes it on Business and Enterprise plans and sells it as an add-on on others, and says it is only available on annual plans and monthly Business plans and above. Without it, a failed webhook stays failed until someone resends it.
Workarounds:
- Turn on Failed Webhook Auto-Retry if your plan includes it.
- Turn on failure alerts, which fire on 4xx and 5xx responses.
- After an outage, open the inbox's Failed Webhooks tab, review the attempts and resend them.
How Hookdeck Can Help: Point the Advanced Webhook at a Hookdeck source. Hookdeck accepts each request immediately and stores it, then delivers it to your service with retries on your own schedule, whatever your Mailparser plan.
More requests than you expected
The Problem: One email produces several webhook requests, and your system creates several records.
Why It Happens: With tabular data, Mailparser can send "an individual HTTP request ... for each parsed table row". Its help centre explains that multiple requests are sent "if tabular data can be found in the parsed results", which can happen when a parsing rule returns several values.
Workarounds:
- If you want one request per email, make sure the What data to send setting is the default, and adjust parsing rules that return multiple values so they return a single block.
- If you want one request per row, include the email-level identifier in each row's body so you can group them.
How Hookdeck Can Help: Hookdeck's request log shows each request Mailparser sent, so you can see exactly how many arrived per email and what each contained.
Your template is the contract
The Problem: Someone edits a parsing rule or resets the template, and your receiver starts failing.
Why It Happens: The body is whatever the template produces. Renaming a parsing rule changes the data, and changing the Content-Type or the What data to send option resets the template.
Workarounds:
- Keep a copy of the template in your own repository next to the receiver.
- Validate required fields on arrival and return a clear
4xxwhen they are missing, so the failure shows in Mailparser's log. - Use Preview Payload after every parsing rule change.
How Hookdeck Can Help: A Hookdeck transformation can normalise field names and types in one place, so a template change needs one fix rather than a change in every consumer.
Best practices
Put an identifier in the template
There is no event ID. Parse a value that is unique per email or row, such as an invoice or order number, and include it in the body so you can deduplicate. See our guide to webhook idempotency.
Use POST with JSON
POST is the default and suits a body. JSON avoids the encoding questions of form data and makes loops over table rows easier to read.
Use the Webhook Delay for slow targets
If the receiving system can't take a burst, the Webhook Delay setting adds "a timed delay between each webhook call". It is a blunt tool, so use it only when the target needs it.
Acknowledge fast and process asynchronously
Return a 2xx once the request is authenticated and stored, then do enrichment and writes in a worker. See why to process webhooks asynchronously.
Conclusion
Mailparser's Advanced Webhooks send whatever your body template describes, each time an email is parsed, with no signature and no event type. Add a secret header and check it in constant time, put an identifier in the template, decide between one request per email and one per row, and make sure retries are covered, by your plan or by your own infrastructure.
Hookdeck Event Gateway checks the secret header, stores each request as soon as Mailparser sends it, and delivers it to your service with retries, so parsed data isn't lost when your endpoint is unavailable.
Get started with Hookdeck for free and handle Mailparser webhooks reliably in minutes.