Overview
Email headers carry rich metadata beyond the visible From, To, and Subject fields. They include:
- Authentication results: SPF pass/fail, DKIM signature status, DMARC policy outcomes
- Routing metadata: Received chain showing the path the message took across mail servers
- Custom application headers:
X-Order-Id,X-Customer-Id,X-Priority,X-Mailer— set by automated sending systems - Content metadata:
Content-Type,MIME-Version,Content-Transfer-Encoding - List management headers:
List-Unsubscribe,List-Idused by mailing lists
JsonHook exposes all headers in the email.headers object as a lowercase-key map, alongside the promoted top-level fields (from, to, subject, date). This makes header inspection a simple property lookup rather than a raw string parse.
Prerequisites
No special prerequisites. All headers are included in every JsonHook delivery payload on all plans. To explore the headers available for your specific email sources:
- Send a few representative emails to a test JsonHook address pointed at webhook.site or your own logging endpoint
- Inspect the
email.headersobject in the delivered payload - Identify the custom headers your sending systems include (e.g.,
x-order-id,x-shopify-event)
Access Every Email Header as JSON
All headers available in every payload. Start free — no credit card needed.
Get Free API KeyStep-by-Step Instructions
Extract and use email headers in your webhook handler:
- Access the headers object:
const headers = payload.email.headers; // All keys are lowercase: "x-order-id", "authentication-results", etc. - Read a specific header:
const orderId = headers["x-order-id"]; const authResults = headers["authentication-results"]; const priority = headers["x-priority"]; - Parse authentication results to verify SPF and DKIM:
const auth = headers["authentication-results"] ?? ""; const spfPass = auth.includes("spf=pass"); const dkimPass = auth.includes("dkim=pass"); if (!spfPass || !dkimPass) { console.warn("Email failed authentication — sender may be spoofed"); } - Use custom headers for reliable routing — they are more stable than subject-line parsing:
const eventType = headers["x-shopify-event-type"]; if (eventType === "order.created") { await handleNewOrder(payload); } - Handle missing headers gracefully with optional chaining and defaults:
const mailer = headers["x-mailer"] ?? "unknown";
Code Example
A handler that uses custom headers for routing and authentication results for security validation:
interface EmailHeaders {
[key: string]: string;
}
function validateSenderAuthentication(headers: EmailHeaders): boolean {
const authResults = headers["authentication-results"] ?? "";
// Require both SPF and DKIM to pass for automated senders
return (
authResults.includes("spf=pass") &&
authResults.includes("dkim=pass")
);
}
function extractCustomData(headers: EmailHeaders) {
return {
orderId: headers["x-order-id"] ?? null,
customerId: headers["x-customer-id"] ?? null,
eventType: headers["x-event-type"] ?? null,
priority: parseInt(headers["x-priority"] ?? "3", 10),
listId: headers["list-id"] ?? null,
};
}
app.post("/webhooks/email", (req, res) => {
// ... verify HMAC signature ...
const { email } = JSON.parse(req.body.toString());
const headers = email.headers as EmailHeaders;
if (!validateSenderAuthentication(headers)) {
// Log but still acknowledge — don't retry unauthenticated emails
console.warn("Unauthenticated sender:", email.from);
return res.sendStatus(200);
}
const data = extractCustomData(headers);
console.log("Custom header data:", data);
res.sendStatus(200);
});Common Pitfalls
Header processing pitfalls:
- Case-sensitive header lookups. JsonHook lowercases all header names. Use lowercase keys in your lookups:
headers["x-order-id"]notheaders["X-Order-Id"]. - Multi-value headers returned as a single string. Some headers (like
Received) can appear multiple times. In the JsonHook payload, multiple values for the same header are joined with a newline. Split onto get individual values. - Treating authentication-results as a boolean. The
Authentication-Resultsheader contains a text description from the receiving server — it is not a boolean pass/fail. Parse its text content carefully and handle different server formats. - Relying on headers that senders can forge. Headers like
X-Priorityand customX-headers are set by the sender and can be spoofed. Only use them for routing decisions when you control the sending system or when a failed authentication check is acceptable. - Not accounting for header folding in raw values. Long headers may be folded across multiple lines in the raw MIME. JsonHook unfolds headers before including them in the JSON — the values you receive are already unfolded.