DEV Community

Cover image for Building an AI-Powered Email Routing Agent with N8N, OpenRouter, and PostgreSQL
Taha Yağız Güler
Taha Yağız Güler

Posted on

Building an AI-Powered Email Routing Agent with N8N, OpenRouter, and PostgreSQL

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:

  1. Read the email
  2. Decide which team handles it (IT, HR, Finance, Legal...)
  3. Forward it manually
  4. 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
Enter fullscreen mode Exit fullscreen mode


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_score between 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
}
Enter fullscreen mode Exit fullscreen mode

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)