Overview
Not every inbound email should trigger the same action. A support email needs a ticket created. A lead notification needs a CRM record. An invoice needs to be forwarded to accounting. Filtering and routing inbound emails to the right handler is as important as parsing them.
JsonHook supports two levels of filtering:
- Address-level routing: Create separate inbound addresses for each email category. Each address has its own webhook URL, so routing happens at the network level — support emails go to the support webhook, order emails go to the order webhook, with no shared handler logic.
- Handler-level filtering: Within a single webhook handler, inspect payload fields (
email.from,email.subject,email.to, custom headers) and dispatch to different processing functions based on the content.
Address-level routing is simpler, more maintainable, and more reliable — it should be your default approach. Handler-level filtering is useful when the routing decision depends on the email content (e.g., routing based on subject keywords).
Prerequisites
Requirements for building a filtered email pipeline:
- A JsonHook account with enough address quota for your routing plan (Starter: 5 addresses, Pro: unlimited)
- A clear mapping of email categories to webhook endpoints
- Understanding of how your email sources deliver to different addresses (e.g., different To addresses on a contact form, forwarding rules in your ESP)
Build Precise Email Routing Pipelines
One address per flow. Every email to the right handler. Start free.
Get Free API KeyStep-by-Step Instructions
Build a filtered inbound email pipeline:
- Map your email flows. List every email category you receive and what action each requires. For example: support@ → create ticket, orders@ → process order, leads@ → create CRM record.
- Create one JsonHook address per category. Each address points to a dedicated webhook handler:
# support address POST /v1/addresses { "webhookUrl": "https://app.com/webhooks/support", "label": "Support emails" } # orders address POST /v1/addresses { "webhookUrl": "https://app.com/webhooks/orders", "label": "Order emails" } - Route email sources to the appropriate address. Update your contact form, ESP settings, and domain forwarding rules.
- Within each handler, add content-based filtering for edge cases:
if (!email.from.endsWith("@trusted-supplier.com")) { console.log("Unexpected sender — skipping"); return res.sendStatus(200); // still return 200 to acknowledge } - Handle unrecognized emails gracefully. Log them with their full payload for manual review rather than discarding silently.
Code Example
A single webhook handler that dispatches based on recipient address and subject content:
import express from "express";
import crypto from "crypto";
const app = express();
app.use(express.raw({ type: "application/json" }));
const DISPATCH: Record<string, (email: any) => Promise<void>> = {
"[email protected]": handleSupport,
"[email protected]": handleOrder,
"[email protected]": handleLead,
};
app.post("/webhooks/all", async (req, res) => {
const sig = req.headers["x-jsonhook-signature"] as string;
const expected = crypto
.createHmac("sha256", process.env.JSONHOOK_SECRET!)
.update(req.body).digest("hex");
if (sig !== expected) return res.sendStatus(401);
const { email } = JSON.parse(req.body.toString());
// Route by recipient
const recipient = email.to[0]?.toLowerCase();
const handler = DISPATCH[recipient];
if (handler) {
await handler(email);
} else {
// Fallback: filter by subject keyword
if (/unsubscribe/i.test(email.subject)) {
await handleUnsubscribe(email);
} else {
console.warn("No handler for email to:", recipient);
}
}
res.sendStatus(200);
});
Common Pitfalls
Filtering pitfalls to avoid:
- Routing by subject instead of recipient address. Subject lines change without notice. Recipient address routing (using dedicated JsonHook addresses) is far more reliable than subject-based dispatch.
- Returning a non-2xx code for filtered emails. If you decide to skip processing an email, still return 200. Returning 4xx or 5xx causes JsonHook to retry, flooding your logs with unnecessary redelivery attempts.
- Not logging filtered emails. Emails that do not match any filter condition should be logged with their deliveryId so you can investigate unexpected traffic patterns.
- Case-sensitive sender matching. Email addresses and domains can be mixed case. Always normalize to lowercase before comparison:
email.from.toLowerCase(). - Blocking on slow filtering decisions. If your filter requires a database lookup (e.g., "is this sender in our allowlist?"), cache the allowlist in memory or use a fast key-value store to avoid slowing down the webhook handler.