Building an AI-Powered Email Routing Agent with N8N, OpenRouter, and PostgreSQL
Every company has that one inbox — the shared one everyone ignores until something explodes. Emails pile up, get misrouted, or simply sit there while the wrong person tries to figure out who should handle it.
I built an AI agent on N8N that solves this completely. It watches an inbox, reads every incoming email, classifies it using an LLM, routes it to the right team, and logs everything to PostgreSQL — fully automated, no human in the loop.
Here's how it works.
The Problem
When customers or internal staff send emails to a shared address, someone has to:
- Read the email
- Decide which team handles it (IT, HR, Finance, Legal...)
- Forward it manually
- Hope it doesn't get lost
This is repetitive, error-prone, and doesn't scale. It's also exactly the kind of work AI is good at.
Architecture Overview
The N8N workflow consists of 7 stages:
Gmail Trigger → Prepare Mail Data → LLM Chain (OpenRouter) → JS Response Parser → Switch → Team Gmail Nodes → Merge → PostgreSQL
Stage 1: Gmail Trigger
The workflow polls the inbox every minute using N8N's Gmail Trigger node. Each new email fires the pipeline automatically.
Stage 2: Prepare Mail Data
Before sending anything to the LLM, a Set node extracts and normalizes the key fields:
-
sender— name + email address -
subject— cleaned header -
content— plain text body -
date— normalized timestamp -
mail_id— unique Gmail message ID -
ai_prompt— formatted string passed to the LLM
Stage 3: LLM Classification via OpenRouter
This is where the intelligence lives. I used N8N's LangChain LLM Chain node with OpenRouter as the model provider, which lets me swap between models (GPT-4o, Claude, Mistral, etc.) without changing the workflow.
The system prompt instructs the model to:
- Classify the email into exactly one category: IT, HR, Finance, or Other
- Assign a priority: High, Medium, or Low
- Write a 3-sentence summary in the original language of the email
- Return a
trust_scorebetween 0.0 and 1.0 indicating classification confidence
The output is strict JSON — no markdown, no preamble, no extra fields:
{
"category": "Finance",
"priority": "Medium",
"summary": "Invoice #12345 payment is overdue.",
"trust_score": 0.87
}
If trust_score falls below 0.70, the model is instructed to fall back to "Other" rather than make a low-confidence routing decision.
Stage 4: JS Response Parser
LLMs occasionally wrap JSON in markdown code fences even when told not to. The Code node strips any `json wrappers, parses the response safely, and handles UTF-8 encoded email headers (a common issue with non-ASCII characters in subjects and sender names).
If parsing fails entirely, the node returns a safe fallback object so the workflow never crashes.
Stage 5: Switch → Team Routing
A Switch node reads the category field and routes to one of four Gmail send nodes:
| Category | Routed To |
|---|---|
| IT | inbox+IT@company.com |
| HR | inbox+HR@company.com |
| Finance | inbox+FINANCE@company.com |
| Other | inbox+OTHER@company.com |
Each forwarded email includes a formatted HTML body with sender, subject, date, priority, trust score, AI summary, and the original content — everything the receiving team needs to act immediately.
Stage 6: PostgreSQL Logging
After routing, all four branches merge and every email is logged to a mail_routing table in PostgreSQL with the following schema:
| Column | Description |
|---|---|
mail_id |
Unique Gmail message ID |
sender |
From address |
subject |
Email subject |
date |
Received date |
category |
AI-assigned category |
priority |
AI-assigned priority |
summary |
AI-generated summary |
trust_score |
Classification confidence |
direct_mail |
Original recipient address |
content |
Full email body |
This gives you a full audit trail, filterable by team, priority, or date range — useful for reporting or debugging misroutes.
What I Learned
OpenRouter is a smart choice here. Routing classification doesn't need GPT-4o — a smaller, cheaper model handles it well. OpenRouter lets you change models with a single config update.
Trust score is underrated. Rather than blindly trusting the classification, having a confidence threshold that falls back to "Other" prevents bad routes for ambiguous emails.
UTF-8 decoding is a real issue. Email headers frequently arrive encoded (e.g. =?UTF-8?B?...?=). You need explicit decoding — N8N doesn't handle this automatically.
Merge before DB write. All four routing branches converge at a Merge node before the PostgreSQL insert. This keeps the logging logic in one place rather than duplicating it across four branches.
What's Next
A few improvements worth adding:
- Confidence-based human escalation — emails below a trust threshold trigger a Slack message asking a human to confirm routing before forwarding
- Reply automation — auto-acknowledge the sender with an estimated response time based on priority
- Dashboard — a simple Grafana view over the PostgreSQL table to visualize routing patterns and volume by team
Wrapping Up
This project took less than a day to build and completely eliminates manual email triage for a shared inbox. The combination of N8N's visual workflow engine, OpenRouter's model flexibility, and PostgreSQL's reliability makes for a surprisingly production-ready stack.
Top comments (0)