Trigger n8n Workflows from Incoming Emails
Build email-powered automations in n8n. Process support tickets, invoices, leads, and more — triggered instantly when emails arrive.
n8n is brilliant for workflow automation. But when it comes to email triggers, you're stuck with the IMAP node — polling every few minutes, wrestling with OAuth tokens, and parsing raw MIME data.
There's a better way: trigger workflows instantly when emails arrive, with clean JSON payloads ready to use.
The IMAP Problem
n8n's built-in IMAP node works, but it has friction:
- Polling delays — 1-5 minute intervals, not real-time
- OAuth headaches — Gmail tokens expire, connections drop
- MIME parsing — raw email data needs manual extraction
- No retry logic — if n8n is down during a poll, emails are missed
For simple use cases, this is fine. For production workflows, you want something more reliable.
Email to Webhook
The solution: convert incoming emails to webhooks. Emails arrive → instant HTTP POST to n8n → workflow runs.
Here's how to set it up with Mailhooks.
Two Ways to Connect
Option A: Mailhooks Community Node
We've built a community node for n8n that makes setup even easier:
# Install in your n8n instance
npm install n8n-nodes-mailhooksOr install via n8n's Community Nodes settings panel.
The node provides:
- Mailhooks Trigger — fires when emails arrive (real-time, no polling)
- Mailhooks — fetch emails, list inboxes, manage hooks via API
Option B: Generic Webhook
If you prefer not to install community nodes, use n8n's built-in Webhook node:
Setup: 5 Minutes
1. Create a Webhook Trigger in n8n
- Create a new workflow
- Add a Webhook node as the trigger
- Set HTTP Method to POST
- Copy the Production Webhook URL
2. Create a Mailhooks Inbox
- Sign up at mailhooks.dev (free tier available)
- Go to Dashboard → Inboxes → Create Inbox
- Name it (e.g., "n8n Support")
- Add a Webhook hook with your n8n URL
- Save
You'll get an email address like [email protected]
3. Test It
Send an email to your new address. Watch n8n execute instantly.
The Payload
When an email arrives, your webhook receives clean JSON:
{
"id": "msg_abc123",
"from": "[email protected]",
"fromName": "Jane Customer",
"to": "[email protected]",
"subject": "Help with my order #12345",
"text": "Hi, I haven't received my order yet...",
"html": "Hi, I haven't received my order yet...
",
"date": "2025-01-28T10:30:00Z",
"attachments": [
{
"filename": "screenshot.png",
"contentType": "image/png",
"size": 45678,
"url": "https://files.mailhooks.dev/..."
}
]
}No MIME parsing. No base64 decoding. Just use {{ $json.from }} in your next node.
Real-World Workflows
Support Ticket from Email
Automatically create tickets when customers email:
[Webhook] → [Notion] Create Page
→ [Slack] Notify #support
Notion node config:
- Database: Support Tickets
- Title:
{{ $json.subject }} - Customer:
{{ $json.from }} - Description:
{{ $json.text }}
Slack node config:
- Message:
📧 New ticket from {{ $json.fromName }}: {{ $json.subject }}
Lead Capture from Contact Form
When leads email your sales address:
[Webhook] → [HubSpot] Create Contact
→ [Gmail] Send Auto-Reply
→ [Slack] Notify #sales
HubSpot node:
- Email:
{{ $json.from }} - First Name:
{{ $json.fromName.split(' ')[0] }} - Lead Source: "Email Inquiry"
- Notes:
{{ $json.text }}
Invoice Processing
Vendors send invoices to a dedicated address:
[Webhook] → [IF] Has PDF Attachment?
Yes → [HTTP Request] Download PDF
→ [Google Drive] Upload to Invoices folder
→ [Airtable] Log invoice record
No → [Slack] "Invoice missing attachment"
Attachment download:
// In HTTP Request node
URL: {{ $json.attachments[0].url }}
Response Format: FileAuto-Responder
Acknowledge emails immediately:
[Webhook] → [Gmail] Send Reply
Gmail node:
- To:
{{ $json.from }} - Subject:
Re: {{ $json.subject }} - Body: "Thanks for reaching out! We've received your email and will respond within 24 hours."
Order Notifications to Slack
E-commerce order confirmations forwarded to your team:
[Webhook] → [Code] Extract Order Details
→ [Slack] Post to #orders
Code node:
const text = $json.text;
const orderMatch = text.match(/Order #(\d+)/);
const totalMatch = text.match(/Total: \$?([\d.]+)/);
return {
orderId: orderMatch ? orderMatch[1] : 'Unknown',
total: totalMatch ? totalMatch[1] : 'Unknown',
customer: $json.from
};Slack message:
🛒 New Order #{{ $json.orderId }}
Customer: {{ $json.customer }}
Total: ${{ $json.total }}
Email to Database
Archive all emails to a database:
[Webhook] → [Postgres] Insert Row
Postgres node:
INSERT INTO emails (from_address, subject, body, received_at)
VALUES (
'{{ $json.from }}',
'{{ $json.subject }}',
'{{ $json.text }}',
'{{ $json.date }}'
)Handling Attachments
Mailhooks provides direct URLs for attachments. Download them in n8n:
Save to Google Drive
[Webhook] → [IF] Has Attachments?
→ [HTTP Request] Download File
→ [Google Drive] Upload
HTTP Request config:
- URL:
{{ $json.attachments[0].url }} - Response Format: File
Google Drive config:
- File Name:
{{ $json.attachments[0].filename }} - Parent Folder: Your folder ID
Process Multiple Attachments
Use a Loop Over Items node:
[Webhook] → [Split In Batches] attachments array
→ [HTTP Request] Download each
→ [Google Drive] Upload each
Filtering & Routing
By Sender
[Webhook] → [Switch] Check sender domain
@vip-client.com → [Priority handling]
@vendor.com → [Invoice processing]
default → [Standard queue]
Switch conditions:
{{ $json.from.endsWith('@vip-client.com') }}
{{ $json.from.endsWith('@vendor.com') }}By Subject
[Webhook] → [Switch] Check subject keywords
contains "urgent" → [High priority]
contains "invoice" → [Accounting]
default → [General inbox]
By Recipient (Catch-All)
With Mailhooks, any address at your domain works:
Route in n8n:
// Switch node
{{ $json.to.includes('support@') }}
{{ $json.to.includes('sales@') }}
{{ $json.to.includes('invoices@') }}Reliability
What if n8n is Down?
Mailhooks queues failed webhooks and retries automatically:
- First retry: 1 minute
- Then: 5 min, 15 min, 1 hour
- Continues for 24 hours
Your emails aren't lost if n8n restarts or has downtime.
Monitoring Deliveries
Check webhook status in Mailhooks dashboard:
- ✅ Delivered — n8n responded 2xx
- 🔄 Pending — queued for retry
- ❌ Failed — all retries exhausted
Compared to IMAP
| Feature | Mailhooks Node | n8n IMAP Node |
|---|---|---|
| Latency | Instant (< 10s) | 1-5 minutes |
| Setup | 2 minutes | 15+ minutes |
| OAuth management | None | Token refresh needed |
| Email parsing | Pre-parsed JSON | Raw MIME |
| Attachments | Direct URLs | Base64 encoded |
| Downtime handling | Queued + retries | Emails missed |
| Multiple addresses | Unlimited catch-all | One per credential |
| Community node | ✅ n8n-nodes-mailhooks | Built-in |
Advanced: Custom Domain
Instead of @yourteam.mailhooks.email, use your own domain:
- Add domain in Mailhooks dashboard
- Set MX records to Mailhooks servers
- Create inboxes with your domain
Now emails to [email protected] trigger your n8n workflows.
Getting Started
- Sign up for Mailhooks — free tier includes 100 emails/month
- Create an inbox with your n8n webhook URL
- Send a test email
- Build your workflow
Total setup: 5-10 minutes. No OAuth, no polling, no MIME parsing.
Need help setting up email-triggered workflows? Check our n8n integration docs or reach out.