🛡️ Portarus: the gateway for AI agents Discover more →
Engineering

Keyword Routing for AI Agents: Split the Inbox

One prompt doing three jobs badly, split into three.

Orlando Quijada · · 4 min read
A single incoming webhook fanning out by keyword — billing messages routed to a billing AI agent, support messages to a support agent, everything else to a default agent
  • One agent for every intent carries all instructions at once — each answer a compromise.
  • Keyword routing is literal match, not intent: first match wins, rest fall through.
  • Routed in front, each agent sees only its own traffic.

The problem

  • One webhook, one agent, every kind of message — billing, support and sales from one prompt.
  • Every instruction rides in one system prompt, every message, so each answer is a compromise.
  • The giant "if the user asks about X, do Y" prompt fights itself as it grows.

The solution

Send each message to the specialist built for it.

Split the inbox into matched (contains a keyword you care about) and unmatched (everything else → a default agent), and route before anyone has to guess.

  1. Define rules: a keyword, a match type, a target URL.
  2. Order narrow keywords above broad ones — first match wins.
  3. Everything else falls through to a capable default, never dropped.

It's literal text matching, not intent detection — keep a solid default for what the keywords miss.

Result: each agent stays small, focused, and easy to change.

See the full fix
A closer look

One webhook, one agent, every kind of message. Your bot answers billing questions, support tickets, and sales leads from the same prompt — so it does all three jobs badly and burns context juggling instructions that don’t apply. Keyword routing sends each message to the agent built for it, before any of them has to guess. Here is how to route by keyword without rebuilding your stack.

Why one agent for everything is a trap

The usual “fix” is a giant prompt with “if the user asks about X, do Y” branches. It works until it doesn’t: the branches fight each other, and one edit to the billing logic quietly breaks support. Routing isn’t about volume — it’s about sending the right message to the right specialist, so each agent stays small and easy to change. Most mixed inboxes split into matched (the message contains a keyword like invoice, refund, cancel) and unmatched (everything else, which should fall through to a default agent).

The manual fix (Option 1)

Route inside your workflow. Add a router right after the trigger — in n8n the Switch node, in Make a router. You read the message, check it against each keyword, and send it down the matching branch. Keep a fallback branch so unmatched messages still get answered.

On a gateway, each agent is a separate endpoint — routing one message never touches another agent.

The catch: routed, but still coupled

Inside a workflow the agents share one canvas and one state, so a route change is a workflow edit that risks the others — and the trigger is counted regardless. To keep agents independent, the routing has to happen in front, before any workflow runs.

Place a gateway layer (Option 2) — faster

A message gateway sits between the sender and your agents and routes by keyword before forwarding. You define rules — each a keyword, a match type, and a target URL — and the gateway sends matched messages straight to that agent’s endpoint and everything else to the default. Each agent only ever receives its own slice of traffic, and adding a route is one rule, not a workflow edit.

Here is what a routed message looks like in the _portarus envelope the agent receives:

"keyword_routing": {
  "matched": true,
  "keywords": ["refund"],
  "route": "https://billing-agent.example.com/webhook"
}

If matched is true, the gateway already decided this message is that agent’s job.

How keyword matching actually works

Be honest about what this is: literal text matching, not intent detection.

  1. Match types. contains catches the keyword anywhere, starts_with anchors it to the front, exact needs the whole message to equal it. Case-insensitive.
  2. First match wins. Rules are ordered by priority and the first match sends the message — so put narrow keywords above broad ones, or the broad rule swallows everything.
  3. No match falls through. A message with none of your keywords goes to the default agent, never dropped.

This is not semantic routing: “I can’t pay” won’t match refund unless the word is there. For fuzzy intent, keep a capable default agent — and merge burst fragments before routing so the keyword matches the full intent, not a stray “hi” that arrived first.

Option 1 (inside) vs Option 2 (in front)

 Route inside the workflowRoute in front (gateway)
Matched → right agent ✓Via a branch ✓Direct to its URL
Unmatched → default ✓Default branch ✓Default target URL
Agents decoupled ✕Share one workflow ✓Separate endpoints
Change a route safely ✕Edit the workflow ✓Edit one rule
Trigger counted first ~Yes, before the router ✓Routed before any run
Control of internals ✓Full ~Abstracted away

It’s one piece of message quality for AI agents.

If option 2 sounds more like you, here’s the short version of how to set it up: give each agent only its own traffic →.

Either way — go make each agent do one job well.

Thanks for reading. 👋

Orlando. -

Found this helpful? Share it with someone who needs it

OQ

Orlando Quijada

Technologist with 15+ years of experience. Developer, SaaS tester, and AI collaboration & optimization specialist. Currently contributing at portarus.com.

Written on

What to read next