Parsing shipping notification emails

Your supplier ships. The carrier emails a tracking number. Somebody copies it into the order so the customer can be told. This is the smallest of the four jobs on this site and the one that runs cleanest — the parse below came back with no flags at all.

Measured on 30 August 2026 against the live service at https://mailmint.app.mintapis.com, from a free account created the same morning. The message and schema are printed in full; rerun it yourself.

Tracking mail into order status #

You want six things out of a despatch mail: the tracking number, who is carrying it, when it left, when it is promised, which order it belongs to, and the link to hand the customer. Get those and the order status updates itself and the "where is my parcel" mail never gets written.

The problem is that these mails come from carriers, from freight forwarders, from suppliers' own systems and from marketplaces, and no two of them lay the information out the same way.

Why it is not a regex #

Tracking numbers look regular enough that everybody starts with a pattern, and the pattern is where it goes wrong:

  • The formats overlap. UPS 1Z numbers are distinctive; DHL, DPD, GLS, Hermes and Royal Mail are runs of 10 to 20 digits that also match order numbers, invoice numbers, customer numbers and phone numbers sitting in the same mail.
  • The number appears more than once. Subject line, body, tracking link, sometimes a returns label with a different number. A pattern takes the first match; the first match is sometimes the return.
  • "Estimated delivery" is written eleven ways and at least three of them are relative — "in 2–4 business days" has no date in it at all.
  • Carrier names are inconsistent. UPS, United Parcel Service, a logo with alt text, or nothing but the tracking URL's domain.

Asking for a described field rather than a pattern moves all of that from your maintenance burden into the parse — and every value comes back with the substring it was read from, so when it is wrong you can see why in one glance rather than debugging a regex against a mail you no longer have.

The schema #

[
  { "name": "tracking_number",    "type": "string", "description": "the carrier tracking number" },
  { "name": "carrier",            "type": "string", "description": "which carrier is delivering" },
  { "name": "shipped_date",       "type": "date",   "description": "date the parcel left the warehouse" },
  { "name": "estimated_delivery", "type": "date",   "description": "expected delivery date" },
  { "name": "order_reference",    "type": "string", "description": "the order this shipment belongs to" },
  { "name": "tracking_url",       "type": "string", "description": "the link to the carrier's tracking page" }
]

Six fields, six descriptions, no templates and nothing positional. The descriptions do real work: they are how order_reference and tracking_number stay apart in a mail that contains both.

A real carrier mail, really parsed #

A despatch notification with a tracking number in the subject and the body, an order reference, a weight, a piece count and a tracking URL. Back in 2,908 ms — the fastest of the four jobs on this site — with flags: [] and needs_review: false:

FieldValueConf.SourceEvidence in the mail
tracking_number1Z999AA101234567840.97rule+llmYour parcel is on its way - tracking 1Z999AA10123456784
carrierUPS0.97rule+llmCarrier: UPS
shipped_date2026-08-290.93llm29 August 2026
estimated_delivery2026-09-020.97rule+llmEstimated delivery: 2 September 2026 by end of day
order_referenceA7-4490210.93llmorder A7-449021
tracking_urlhttps://www.ups.com/track?tracknum=1Z999AA101234567840.93llmthe same URL, verbatim

Three things in that table are worth a second look. The tracking number and the order reference are both alphanumeric identifiers in the same short mail and neither leaked into the other's field. 2 September 2026 by end of day became the date 2026-09-02 without the trailing words. And rule+llm on four of the six fields means a deterministic rule and the model independently produced the same value — that agreement is what the 0.97 is; the model alone cannot raise a confidence, it can only lower one.

Joining it back to the order #

The despatch mail is only useful once it finds its order, and order_reference is the join key. Two practical notes:

  • If the reference is missing from the mail entirely — some carrier notifications carry only the tracking number — the field comes back null rather than guessed, and you join on the tracking number you stored when you booked the shipment instead.
  • The full message object always carries the sender, the subject, and SPF, DKIM and DMARC results. "This despatch mail is really from the carrier" is a question worth asking before a tracking link goes out to a customer, and the answer travels with the parse.

Then either the webhook posts it to your order service as it arrives, or you poll GET /v1/events, or an n8n workflow moves it — n8n-nodes-mailmint on n8n Cloud or self-hosted n8n, with an HTTP Request node against POST /v1/parse as the no-community-node route.

When not to use this #

  • You booked the shipment yourself. Then you already have the tracking number from the carrier's API, and you should be using their tracking API for status too — it tells you about delays, which an email sent once at despatch never will.
  • You only need to know a parcel exists. A filter rule in the mailbox is free.
  • One supplier, one template. A regular expression is fine and it will not cost you $9 a month. Come back when the second supplier arrives with a different layout.
  • Live tracking events. MailMint parses the despatch mail. It does not follow the parcel; "out for delivery" and "delivery attempted" come from the carrier, not from us.

What we cannot claim #

MailMint has paying customers, but no customer logo, testimonial or case study is quoted without permission. There is no published uptime history, SLA or SOC 2 report. One region, one instance. This is one carrier mail, not a benchmark across carriers — and the clean run above is the easiest of the four jobs on this site, which is exactly why the invoice page shows a field that came back at 0.25 instead. Relative dates ("in 2–4 business days") are not resolved to a calendar date. PDF attachment contents are not read.

Try it on your own worst email #

Not a demo message — the one your current parser gets wrong. Paste its subject and body into POST /v1/parse and read the confidence and the evidence span on each field. /v1/parse stores nothing, needs no inbound address and no DNS record, and the free plan is 300 parsed emails a month with no card, which is enough to run a real low-volume workflow rather than only to look at one.

Get an API key   Read the quickstart   API reference

The other three #

Invoice emailsSupplier invoices into ledger rows, with the arithmetic checked.
Order confirmationsOrder number, totals and every line item, reconciled against the total.
Lead & contact-form emailsName, company, phone and budget into the CRM, with the doubtful ones held back.

Comparisons instead: what an email parsing API has to get right · vs Mailparser · vs Parseur · vs Docparser · vs Zapier Parser