biza.email
Sign inStart free
Back to all posts
·4 min read

Building an Invoice Intake Pipeline With Regex Triggers and Webhooks

A concrete recipe for routing incoming invoices automatically: match by sender domain, extract the PO number with a snippet, and push to your ERP via webhook — all inside Biza's automations engine.

automationswebhooksinvoicesintegrations

Every accounts-payable team has the same quiet problem: supplier invoices land in a shared mailbox, someone downloads the PDF, someone else keys the PO number into the ERP, and somewhere in that chain a line item gets fat-fingered or the email gets marked read and forgotten. The fix isn't a $600/month middleware subscription. It's a three-step automation you can wire up in an afternoon.

This walkthrough builds a complete invoice intake pipeline using Biza's automations engine: a regex-based trigger to catch the right emails, a JavaScript snippet to pull structured fields from the message body, and a webhook to push that data straight into your accounting system.

Step 1 — Catch the right emails with a sender-domain trigger

Start by creating a new automation in Settings → Automations → New Rule.

The first condition should match on the sender's domain. Most suppliers send invoices from a consistent address pattern ([email protected], [email protected], billing@*). Rather than listing every supplier individually, use a regex against the from field:

Trigger: Email received
Condition: from   matches regex   ^.+@(supplier\.com|vendor\.co|billing\..+)$

If your supplier list is long or changes often, a second condition narrows the net further:

AND: subject   matches regex   (invoice|inv|bill|statement)\s*#?\s*\d+
                               (case-insensitive)

That pattern catches subjects like Invoice #4421, INV-2024-0093, and Bill for April services while ignoring newsletters and support tickets from the same domains.

Add a third condition if your team uses a dedicated alias:

AND: to   is   [email protected]

Combining all three means only real supplier invoices fire the rule. Everything else flows through normally.

Step 2 — Extract fields with a JS snippet

Once the trigger fires, you need structured data: PO number, invoice number, total amount, due date. Some of this lives in the subject line; most of it is in the body.

Add a Run snippet action and paste something like this:

// Available in scope: email.subject, email.bodyText, email.from, email.receivedAt

const patterns = {
  invoiceNumber: /inv(?:oice)?\s*#?\s*([A-Z0-9\-]+)/i,
  poNumber:      /p\.?o\.?\s*(?:number|num|#)?\s*:?\s*([A-Z0-9\-]+)/i,
  totalAmount:   /(?:total|amount due|balance due)\s*:?\s*\$?([\d,]+(?:\.\d{2})?)/i,
  dueDate:       /due\s+(?:date|by|on)?\s*:?\s*(\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4})/i,
};

const extracted = {};
for (const [key, re] of Object.entries(patterns)) {
  const match = email.bodyText.match(re) ?? email.subject.match(re);
  extracted[key] = match ? match[1].replace(/,/g, '') : null;
}

// Also capture sender domain for supplier lookup
extracted.supplierDomain = email.from.replace(/^.+@/, '');
extracted.receivedAt     = email.receivedAt;
extracted.emailId        = email.id;

return extracted;

The snippet returns a plain object. The fields it produces — invoiceNumber, poNumber, totalAmount, dueDate, supplierDomain — are available as variables in every subsequent action in the same rule.

A few practical notes:

  • Regex coverage isn't 100%. Some suppliers send PDFs with no body text. For those, the fields come back null. Decide in your webhook handler whether a null PO number should create a draft record or fire a Slack alert for manual review — don't let the pipeline silently drop the invoice.
  • Test against real samples first. Copy three or four representative invoice bodies into the snippet's test console before going live. It takes five minutes and saves a Friday firefight.
  • Currency. The pattern above strips commas but not currency symbols or three-letter codes. If your suppliers mix USD and EUR, add a second capture group or a normalization step before the webhook.

Step 3 — Push to your ERP via webhook

Add a Send webhook action after the snippet. Configure it as a POST to your accounting system's inbound endpoint:

Method:   POST
URL:      https://erp.yourcompany.com/api/invoices/intake
Headers:
  Content-Type: application/json
  Authorization: Bearer {{env.ERP_API_TOKEN}}

Body:

{
  "source":          "email",
  "email_id":        "{{snippetResult.emailId}}",
  "invoice_number":  "{{snippetResult.invoiceNumber}}",
  "po_number":       "{{snippetResult.poNumber}}",
  "total_amount":    "{{snippetResult.totalAmount}}",
  "due_date":        "{{snippetResult.dueDate}}",
  "supplier_domain": "{{snippetResult.supplierDomain}}",
  "received_at":     "{{snippetResult.receivedAt}}"
}

The {{env.ERP_API_TOKEN}} reference pulls from your workspace's encrypted environment variables — you set those once in Settings → Automations → Environment, and they never appear in the rule editor or logs in plaintext.

If your ERP doesn't have a REST endpoint for this (some older systems don't), the same body can go to a lightweight relay: a single Cloudflare Worker or a small Express route that translates JSON into whatever your system expects — a database insert, an SFTP drop, a SOAP call. The automation doesn't care.

Closing the loop — labeling and archiving

Add two more actions at the end of the rule:

  1. Apply labelAP / Processed so the AP team can see at a glance what's been handed off.
  2. Mark as read (optional) — only do this if your team treats the inbox as a queue and marks things manually; don't do it if humans still review before the ERP record is confirmed.

You can also add a conditional branch: if snippetResult.poNumber is null, skip the webhook and instead move the email to a AP / Needs Review label for manual handling. That keeps your ERP data clean without silently losing invoices.

What you end up with

Incoming invoice email → matched by sender + subject regex → fields extracted in ~50ms → webhook fired to ERP → email labeled and archived. The whole chain runs in a few seconds, no human in the loop for the 80% of invoices that arrive in a clean format.

The suppliers that send malformed or image-only emails end up in a review queue with a label instead of sitting unread in a shared mailbox. That's a tractable problem. The ones that fell through the cracks entirely weren't.

No Zapier. No n8n server to maintain. The automation lives next to the email it processes.