MailMint for n8n

Every other email parser makes you leave n8n, open their web app, upload a sample and click on the parts of it you want — then come back and receive whatever they decided to send. This node does the whole thing on the canvas: name a field, give it a type and a sentence of description, hit Execute step, see the value.

Packagen8n-nodes-mailmint · v0.1.0 · MIT
Runtime dependencieszero
NodesMailMint (11 actions) + MailMint Trigger

Publication status #

n8n-nodes-mailmint has never been published to npm. npm view n8n-nodes-mailmint returns a 404 today. That means the Settings → Community Nodes → Install route below does not work yet, and n8n Cloud — which only accepts verified community nodes — cannot install it at all.

Every screenshot on this page is the node running in a real n8n instance from a local build, not a mock-up. The install instructions are here because they are what will be true the moment it is published, and they are marked as not-yet rather than quietly presented as working. What to do instead, today.

Install #

Today — build the tarball and install that

Until the package is published this is the honest route, and it is the one every screenshot on this page was taken through:

in packages/n8n-node
npm install --ignore-scripts
npm run build
npm pack                       # -> n8n-nodes-mailmint-<version>.tgz

cd ~/.n8n/nodes
npm install /path/to/n8n-nodes-mailmint-<version>.tgz

Then restart n8n.

Once it is published

Settings → Community Nodes → Install, then enter n8n-nodes-mailmint; or cd ~/.n8n/nodes && npm install n8n-nodes-mailmint from the CLI. Neither works yet — see above.

Once installed, both nodes appear in the node panel under their own names. The trigger is found by searching the trigger panel for MailMint:

The n8n "What triggers this workflow?" panel with MailMint typed into the search box, showing one result: MailMint, "Starts the workflow when MailMint has parsed a new email".
The trigger panel. The node is marked “Via npm” by n8n because it is a community node.
The n8n node details panel for MailMint showing Actions (11), grouped into Mailbox actions — Create a mailbox, Delete a mailbox, Get many mailboxes, Reparse every message in a mailbox, Update a mailbox — Message actions — Download an attachment, Get a message, Get many messages, Get the raw message, Reparse a message — and Parse actions — Parse an email.
Eleven actions across three resources, plus one trigger. Reparse every message in a mailbox is the one worth knowing about — see below.

The credential #

  1. In n8n, add a MailMint API credential.
  2. API Key — the key for your MailMint account. It starts with mm_live_.
  3. Base URL — the root URL of the MailMint API you are talking to, with no trailing slash. There is no default, because there is no hosted MailMint to default to. Use your own deployment, or http://host.docker.internal:3100 when the API runs locally and n8n is in Docker.
  4. Save.

The credential tests itself against GET /v1/usage, so you get a real green tick before you build anything rather than an “untested” badge:

The n8n credential dialog for a MailMint account, credential type MailMint API, showing a green "Connection tested successfully" banner, a masked API Key field and a Base URL field.
“Connection tested successfully” means the key is valid and the account exists — the test is a real authenticated call, not a format check. This one was taken against a MailMint API running locally, because there is no hosted one to point it at.

First workflow — mail you already have #

You do not need a MailMint address to start, which matters, because the hosted address is not live yet. If mail is already reaching n8n — through the built-in Email Trigger (IMAP), through Gmail, through anything — put a MailMint node after it and it parses what arrives.

  1. Add an Email Trigger (IMAP) node and point it at your mailbox. Any Format works.
  2. Add a MailMint node after it. It opens on Parse → Parse Email with Input: Automatic, which is already correct.
  3. Under Fields, click Add Field three times and fill them in.
  4. Execute step.

What “Automatic” actually does. It takes the raw .eml off the binary field when the IMAP node is set to RAW, and the subject / textPlain / textHtml fields off the JSON when it is set to Resolved or Simple. It will not mistake an attached PDF for the message. The other Input modes — Binary File, Raw MIME Text and Fields — are there for when you want to be explicit.

The schema editor #

This is the part that does not exist anywhere else. Each field you add has five inputs:

InputWhat to put in it
NameThe JSON key you want back, e.g. invoice_number.
TypeOne of thirteen. See the table below.
DescriptionThe single biggest lever on accuracy. Write it the way you would explain the field to a new colleague — “grand total including tax”, not “total”.
HintThe label the mail actually uses: “labelled Total or Amount Due”.
RequiredA toggle. A required field that is missing still comes back null — it is flagged, never fabricated.
The MailMint node's Fields editor in n8n. Field 2 has Description "grand total including tax", Hint "labelled Total", Name "total", Required off and Type Number. Field 3 has Description "when payment is due", Hint "labelled Due", Name "due_date", Required off and Type Date.
Three fields, defined on the canvas, next to the workflow that consumes them. No web app, no sample upload, no clicking on a rendered email.
Type in the dropdownYou get backExtra input
String · Email · URL · Phone Numberthe text as written
Number · Integera number
Currency{ "amount": 31.5, "currency": "USD" }
Date2026-09-08
Date and TimeISO-8601, UTC
Booleantrue / false
Enumone of your values, or nullOptions, comma separated
Arraya listItem Type
Objecta nested objectSub-Fields, a JSON array of field definitions

A value that cannot be coerced comes back as null with a type_error flag. It is never invented, and never a guess string like “N/A”.

Schema also takes From a Mailbox, to reuse the schema saved on one of your MailMint mailboxes, and JSON, to compute the whole schema in an earlier node.

What comes out #

The MailMint node after Execute step, with one output item shown as a table: invoice_number INV-2292, total 132, due_date 2026-09-15, _needs_review false, and a _meta column listing id, message_id, received_at, subject, from_email, type, flags, spf, dkim, dmarc and more.
One item out. Your fields at the top level, _needs_review beside them, and everything else under _meta.
{
  "invoice_number": "INV-2291",
  "total": 31.5,
  "due_date": "2026-09-08",
  "_needs_review": false,
  "_meta": {
    "subject": "Invoice INV-2291 from Acme Ltd",
    "from_email": "billing@acme.example",
    "received_at": "2026-08-25T09:14:03.221Z",
    "type": "invoice",
    "needs_review": false,
    "flags": [],
    "attachment_count": 1,
    "attachment_names": ["invoice-2291.pdf"]
  }
}

That is Simplify, which is on by default, because the next node is nearly always a Google Sheets, a Postgres or an IF. Turn it off and you get the full message object: headers, both bodies, tables, detected amounts and dates, and per-field confidence with the evidence each value was read from.

Line items — one item per row #

The loudest complaint in this whole category is “the mail had forty rows and I got one”. Set Output to One Item Per Line Item and one email becomes one n8n item per invoice line, with the header fields repeated on each.

The MailMint node with Simplify on, Output set to One Item Per Line Item, and an empty Line Items From field. The output panel shows 3 items, each repeating invoice_number INV-2292, total 132 and due_date 2026-09-15.
One email in, three items out — the header fields repeated on every row.
{ "invoice_number": "INV-2292", "total": 132,
  "Item": "Widget", "Qty": "3", "Amount": "$27.00",
  "_row_index": 0, "_row_count": 3, "_line_items_truncated": false }
  • It finds the rows on its own: an array field you defined first, then the largest table found in the body. Name a specific one in Line Items From if you want a particular table.
  • _row_count travels on every row and _line_items_truncated says so out loud when the table came back short. A short array can never be silent.
  • A message with no rows still produces exactly one item, with _row_count: 0. Nothing is ever silently dropped.
  • pairedItem is set on every row, so n8n can still trace all forty items back to the one email they came from.

For comparison: Zapier’s documented answer to the same requirement is to hand-write {{shotnumberOne}}{{shotnumberN}} against a hard cap of 15 templates, and Make needs an Iterator plus an Array Aggregator and breaks past two columns.

Routing what needs a human #

Every field carries a confidence, the source it came from and the verbatim evidence it was read out of. Turn on Options → Include Confidence:

"_confidence": {
  "total": { "confidence": 0.97, "source": "rule+llm", "evidence": "Total: $31.50" }
}

_needs_review sits at the top level of every simplified item. Turn on Route Messages Needing Review Separately and the node grows a second output: anything with a missing required field, a low-confidence value, a type it could not coerce, or evidence it could not find in the mail goes down Needs Review instead of Parsed.

The same MailMint node with the Route Messages Needing Review Separately toggle switched on, and the output panel now showing a warning indicator beside OUTPUT.
One toggle. No IF node, no expression, no matching on a sentence.

Line items follow their message: forty rows from a doubtful invoice all go down the same branch together, rather than being split across two.

The same setting exists on the trigger, so mail that needs a human can be routed the moment it arrives.

Attachments #

Filenames, content types, sizes and checksums are on every simplified item already, under _attachments, with no option to turn on. The bytes are the one thing behind a switch (Options → Include Attachment Bytes), because a single PDF is usually larger than the rest of the message put together. To get a file as real n8n binary data, use Message → Download Attachment.

Reading text out of a PDF is built but not yet wired in. The node knows how to fan line items out of _attachments[].extracted, the API reserves that slot, and the extractor itself exists — but nothing connects them yet, so the slot is empty today. An invoice whose detail lives only inside the attached PDF gives you the file and its metadata, not its contents.

MailMint Trigger #

For mail that should come to MailMint rather than through your own IMAP. One node, two modes.

The MailMint Trigger node parameters: Webhook URLs, Credential MailMint account, Poll Times with Mode Every Minute, Delivery set to Webhook, Mailbox Name or ID, Simplify on, Output One Item Per Message, Route Messages Needing Review Separately off, Filters and Options.
The Delivery parameter picks the mode, and Webhook is the default. Fetch Test Event, top right, returns your most recent real message without moving the polling cursor. (The mailbox address in the screenshot has been changed to a reserved example domain.)

Webhook — the default

It registers itself. On activation it sets the mailbox’s webhook_url to this workflow’s URL and installs a signing secret it generates for you; on deactivation it clears them. Every incoming delivery has its x-mailmint-signature HMAC verified before the workflow runs — a body that does not verify is answered 401 and never starts anything. Instant, and there is nothing to configure beyond picking the mailbox.

Options → Signature Tolerance (Seconds) defaults to 300, and Options → Webhook Secret lets you pin your own instead of letting the node generate one.

The MailMint Trigger node after a real webhook delivery, showing one output item with invoice_number INV-2291, total 31.5, due_date 2026-09-08, _needs_review false, and a _meta column listing spf pass, dkim pass and dmarc pass.
A real delivery arriving on the trigger: signature verified, message parsed, one item on the canvas. spf, dkim and dmarc sit in _meta.

Polling

For an n8n the internet cannot reach. It reads GET /v1/events on your poll schedule and remembers the cursor on the node. On first activation it seeds the cursor and emits nothing, so switching a workflow on never floods it with a week of old mail.

Filters

Both modes take the same FiltersNeeds Review Only, From Sender (an address or a domain) and Mailbox Name or ID — and the same Output, Simplify and Route Messages Needing Review Separately settings as the main node.

All eleven actions #

ResourceOperationWhat it does
ParseParse EmailExtract your fields from an email the workflow already has. Nothing is stored.
MessageGetOne parsed message by ID
Get ManyList parsed messages, filtered by mailbox, date or needs-review
Get RawThe original RFC822 message as binary .eml
Download AttachmentOne attachment as binary data
ReparseRe-run one message, optionally against a different schema
MailboxCreateA new inbound address with a schema on it
Get ManyThe addresses on your account
UpdateName, schema, webhook URL, webhook secret
Reparse MessagesRe-run every stored message after you fix the schema, with a Dry Run first
Delete

Reparse Messages is the one worth knowing about. Zapier’s answer to “can I replay old mail” is verbatim “there is no way to replay them”, and Mailparser stops at the last 300. Fix your schema, dry-run it to see what would change, then run it for real. Re-delivery is a separate switch from re-parsing, so nobody accidentally fires a month of webhooks at their own workflow.

The main node is marked usableAsTool, so an n8n AI Agent can call MailMint directly as a tool.

Errors #

Every failure carries the API’s own message and its hint, plus the item index that failed. With Settings → Continue On Fail on, the failing item becomes:

{ "error": { "code": "message_not_found", "message": "…", "hint": "…", "httpCode": "404" },
  "errorMessage": "…" }

so an IF node can branch on {{ $json.error.code }} instead of matching on a sentence. The full code list is in the API reference.

What works on n8n Cloud today #

n8n Cloud only accepts verified community nodes, and n8n-nodes-mailmint is not published at all yet, let alone verified. Until it is, an HTTP Request node does the same job from any n8n:

Method:            POST
URL:               {{ $env.MAILMINT_URL }}/v1/parse
Authentication:    Generic → Header Auth
  Name:            Authorization
  Value:           Bearer mm_live_…
Send Body:         on   (JSON)
Body:
  {
    "subject": "{{ $json.subject }}",
    "text":    "{{ $json.textPlain }}",
    "html":    "{{ $json.textHtml }}",
    "schema": [
      { "name": "invoice_number", "type": "string", "description": "the invoice or reference number" },
      { "name": "total",          "type": "number", "description": "grand total including tax" }
    ]
  }

You lose the schema editor, the line-item fan-out and the second output — everything else, including the confidence and evidence on every field, is identical, because the node is a thin client over the same endpoint.