n8n-nodes-imap-agent
v0.4.0
Published
n8n community node for IMAP mailbox access with first-class AI Agent (usableAsTool) support. Read, search, move, flag and download emails from any IMAP server via imapflow + mailparser.
Maintainers
Readme
n8n-nodes-imap-agent
An n8n community node for IMAP mailbox access with first-class AI Agent support (usableAsTool: true). Read, search, move, flag and download emails from any IMAP server — either as a regular workflow node or as a tool driven by the n8n AI Agent node.
Built on top of
imapflow(modern, Promise-based IMAP) andmailparserfor RFC822 MIME parsing.
Why another IMAP node?
The existing community node n8n-nodes-imap works well for classic workflows, but was not designed for the n8n AI Agent tool protocol. This package is an independent implementation with three priorities:
- AI-ergonomic descriptions. Every resource, operation and parameter has a description written for an LLM: what the tool does, when to pick it, what each filter means.
- Flat, JSON-shaped search input. IMAP search criteria (
SINCE,UNSEEN,FROM, etc.) are exposed as a collection with one field per criterion — the LLM fills a JSON object, the node translates it to an IMAP query. - Binary-clean downloads. Attachments and
.emlfiles come through as n8n binary data, ready to hand off to Paperless-ngx, S3, a file node, or back to the agent.
Installation
In the n8n UI (self-hosted)
Settings → Community Nodes → Install → package name n8n-nodes-imap-agent → Install.
As a custom node (link or manual install)
# in your n8n instance
cd ~/.n8n/custom
npm install n8n-nodes-imap-agent
# restart n8nDocker
Mount the custom directory and install inside the container, or extend the image:
FROM n8nio/n8n:latest
USER root
RUN cd /home/node/.n8n/custom && npm install n8n-nodes-imap-agent
USER nodeCredentials
Create a credential of type IMAP API.
| Field | Description | Example |
| --- | --- | --- |
| Authentication | Password or OAuth2 (XOAUTH2) | Password |
| Host | IMAP server hostname | imap.gmail.com |
| Port | 993 (implicit TLS) or 143 | 993 |
| Use TLS | enable for port 993 | true |
| User | mailbox user / email | [email protected] |
| Password | account or app password | xxxx-xxxx-xxxx-xxxx |
| Access Token | OAuth2 token (only in OAuth2 mode) | — |
| TLS Options → Allow Self-Signed | set false for self-hosted Dovecot with a private CA | true |
| TLS Options → Min Version | lowest TLS version to negotiate | TLSv1.2 |
| Advanced → Connection Timeout | socket timeout in ms | 30000 |
| Advanced → Disable Compression | turn off IMAP COMPRESS=DEFLATE | false |
Gmail / Google Workspace
- Enable 2FA and use an App Password — Google refuses plain passwords over IMAP since 2022.
- Alternatively use
OAuth2 (XOAUTH2)and hand over a fresh access token. ⚠️ This node does not refresh tokens on its own — access tokens typically expire after 1 hour. Pair with an upstream HTTP Request / OAuth2 node that refreshes, then pass the token in via expression like={{ $json.access_token }}. A stale token surfaces as "authentication failed".
Microsoft 365 / Outlook.com
- Basic auth is deprecated. Use
OAuth2 (XOAUTH2)with a Graph-registered app (IMAP.AccessAsUser.All scope).
Self-hosted (Dovecot, Stalwart, Mailcow)
- Plain password + TLS 1.2 on port 993.
- For internal CAs: set
Allow Self-Signed Certificatestofalseonly after you've installed your CA root in the n8n container — otherwise usetrue.
Operations
Resource: Email (10)
| Operation | What it does | Typical AI use |
| --- | --- | --- |
| Search | IMAP SEARCH with FROM/SUBJECT/SINCE/UNSEEN/FLAGGED/TEXT/BODY filters | "Find ungelesene Mails mit 'Rechnung' von letzter Woche" |
| Get | Fetch one message by UID, parsed subject/body/headers, optional attachments | "Lies Mail 4217 und fasse zusammen" |
| Move | Move UIDs → folder (batch-fähig) | "Verschiebe alle 12 Rechnungen nach /Archive/2026/Rechnungen" |
| Copy | Copy UIDs → folder (batch) | — |
| Delete | \Deleted + best-effort EXPUNGE (batch, returns {deleted, expunged, uids, requestedCount} — expunged: false means the server rejected UID EXPUNGE, flag is set, server expunges on next folder close) | — |
| Mark Read / Mark Unread | \Seen flag toggle (batch) | — |
| Flag / Unflag | \Flagged (star) or custom keyword (batch) | "Markiere als Important" |
| Append | Upload raw RFC822 to a folder (from text or binary input) | "Save draft" |
Resource: Mailbox (7)
List, Create, Delete, Rename, Status, Quota, Test Connection.
Agents should run Mailbox: List once before addressing specific folders, so they know the separator (/, ., /[Gmail]/) and which folders exist.
Batch UIDs
All write operations (Move, Copy, Delete, Mark Read, Mark Unread, Flag, Unflag) accept the batch UIDs field — a single number, a comma list, or an IMAP range:
| Input | Meaning |
| --- | --- |
| 42 | single message |
| 1,5,9 | three messages |
| 100:200 | 101 messages (range, inclusive) |
| 1:* | all messages in the folder |
| 1,5,9:12,20 | mixed |
One call → one IMAP round-trip. A classification workflow that moves 50 mails no longer needs 50 execute() iterations.
Error Handling
When the node is set to Continue On Fail, error items now include full context so a downstream IF / Switch node can branch cleanly:
{
"error": "IMAP flag add failed: no messages matched \"4217\" in \"INBOX\"",
"resource": "email",
"operation": "flag",
"mailbox": "INBOX",
"uids": "4217"
}Trigger: IMAP Trigger
Separate node (group trigger) that starts a workflow when the watched mailbox changes. Uses IDLE when the server supports it, polling otherwise.
| Property | Default | Notes |
| --- | --- | --- |
| Mailbox | INBOX | Folder path (server delimiter) |
| Events | newMessage | Multi-select: newMessage (EXISTS), expunged (EXPUNGE), flagsChanged |
| Mode | auto | auto picks IDLE if advertised, else poll. idle forces (errors if unsupported). poll forces polling. |
| Poll Interval | 60 s | Only used in poll mode |
| Filters | — | Optional client-side from / to / subject substring filter applied AFTER fetch |
| Fetch Options | — | includeBody, includeHeaders, includeAttachments, maxAttachmentSizeMB (default 25) |
| Manual Trigger Sample Size | 5 | When you click "Execute Node" in the editor, emit N recent messages for testing |
Behaviour:
- No backlog replay. On first activation (or after UIDVALIDITY change), the trigger sets its watermark to the current
UIDNEXTand only emits subsequent messages. - State persistence.
lastUidNext+uidValiditysurvive workflow restarts viagetWorkflowStaticData. UIDVALIDITY change (mailbox re-indexed) resets the watermark without replaying. - Reconnect. Exponential backoff (250 ms → 30 s cap) on connection loss.
client.on('close' | 'error')triggers a re-connect; the catch-up fetch after reconnect emits anything that arrived while offline. - Manual test. Clicking "Execute Node" in the editor emits the N most recent messages without touching production state — safe to test an activated workflow.
Resource: Download (2)
Download Attachments— all attachments of a UID into binary slots, with optional filters:- Filename Contains — case-insensitive substring match on the attachment filename.
- MIME Type — exact (
application/pdf), type-wildcard (image/*), or full-wildcard (*/*/*). A bare type likeimagedoes not match — always include a subtype or wildcard. - Max Attachment Size (MB) — default
25. Guards n8n workers against OOM when a mail carries a 500 MB file. Set0to disable.
Download EML— full raw RFC822 message as a single.emlbinary.
Example AI Agent workflow
Paste the JSON below into Workflows → Import from clipboard. It's a minimal "triage my inbox" agent: the AI Agent is wired to an OpenAI chat model and is given the IMAP node as a tool. The system prompt is kept short on purpose — the tool descriptions in this package do the heavy lifting.
Replace
imap-ai/openai-credwith your actual credential IDs after import.
{
"name": "IMAP AI Triage (demo)",
"nodes": [
{
"parameters": {},
"id": "1a",
"name": "When chat message received",
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"typeVersion": 1,
"position": [240, 300]
},
{
"parameters": {
"options": {
"systemMessage": "Du bist ein E-Mail-Assistent. Nutze das Tool 'IMAP (AI)' um Mails zu suchen, zu lesen oder zu verschieben. Antworte knapp auf Deutsch."
}
},
"id": "1b",
"name": "AI Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 1.7,
"position": [520, 300]
},
{
"parameters": {
"model": "gpt-4o-mini",
"options": {}
},
"id": "1c",
"name": "OpenAI Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1,
"position": [520, 500],
"credentials": { "openAiApi": { "id": "openai-cred", "name": "OpenAI" } }
},
{
"parameters": {
"resource": "email",
"operation": "search",
"mailbox": "INBOX",
"searchFilters": { "unseen": true },
"options": { "limit": 20, "newestFirst": true }
},
"id": "1d",
"name": "IMAP (AI)",
"type": "n8n-nodes-imap-agent.imap",
"typeVersion": 1,
"position": [800, 500],
"credentials": { "imapApi": { "id": "imap-ai", "name": "My IMAP" } }
}
],
"connections": {
"When chat message received": {
"main": [[{ "node": "AI Agent", "type": "main", "index": 0 }]]
},
"OpenAI Chat Model": {
"ai_languageModel": [[{ "node": "AI Agent", "type": "ai_languageModel", "index": 0 }]]
},
"IMAP (AI)": {
"ai_tool": [[{ "node": "AI Agent", "type": "ai_tool", "index": 0 }]]
}
}
}Example chat turns once the workflow is running:
- "Habe ich neue Rechnungen seit Montag?" → Agent calls
Email / Searchwith{ subject: "Rechnung", since: "2026-04-20", unseen: true }. - "Zeig mir Mail 1423" →
Email / Getwithuid: 1423. - "Verschiebe sie nach Archive/2026/Rechnungen" →
Email / Movewith that UID and the target folder.
Local development
git clone https://github.com/Daisytwo/n8n-nodes-imap-ai.git
cd n8n-nodes-imap-ai
npm install
npm run build # tsc + gulp build:icons
npm run lint # ESLint with n8n-nodes-base rules
npm test # Jest unit tests for helpers
npm run dev # tsc --watchCI runs lint, build and test on every push and PR against main via GitHub Actions (.github/workflows/ci.yml, Node 20 & 22).
Then link into a local n8n instance:
# inside this repo
npm link
# in your n8n custom-nodes dir (create if missing)
mkdir -p ~/.n8n/custom && cd ~/.n8n/custom
npm init -y 2>/dev/null || true
npm link n8n-nodes-imap-agent
# start n8n — it auto-discovers linked packages
n8n startOpen http://localhost:5678, create an IMAP API credential, add an IMAP (AI) node, or wire it as a tool under an AI Agent node.
Implemented / omitted / known limits
Implemented (19 operations + trigger)
- Mailbox (7): List, Create, Delete, Rename, Status, Quota, Test Connection
- Email (10): Search, Get, Move, Copy, Delete, Mark Read, Mark Unread, Flag, Unflag, Append
- Download (2): Download Attachments (with filename/MIME/size filter), Download EML
- Trigger: IMAP Trigger with IDLE (push) + polling fallback, watermark-based dedupe via UIDNEXT/UIDVALIDITY
Deliberately not included
- Gmail-only thread ops (
X-GM-THRID). Non-portable across servers. - OAuth2 refresh flow. Credentials take a pre-issued access token — pair with an upstream OAuth2 credential node for refresh.
Known limits
hasAttachmentfilter is a size heuristic (> 50 KB), not an exact BODYSTRUCTURE scan.- No connection pool across
execute()runs — a fresh IMAP connection per node execution, one socket shared across all items inside that execution. - Unit tests cover the pure helpers (
buildSearchQuery,parseRawEmail,matchesMime). Integration testing against Gmail / Dovecot / Stalwart is manual — there is no mock IMAP server in the suite. Deleteissues\Deleted+ best-effort EXPUNGE (expunged: falsein the return when the server refuses UID EXPUNGE — the flag is still set). Gmail behaviour differs (moves to Trash).- Attachment size cap defaults to 25 MB — larger attachments are silently skipped during
Download Attachments. Override viaMax Attachment Size (MB) = 0to disable.
License
MIT ©
