CrewAI Tutorial (TypeScript): building conditional routing for advanced developers
This tutorial shows how to build a CrewAI workflow in TypeScript that routes tasks conditionally based on the input, so different requests take different paths through your agent graph. You need this when one crew cannot handle every case the same way, like sending simple customer queries to one agent and escalations to another.
What You'll Need
- •Node.js 18+ and a TypeScript project
- •
crewaiinstalled in your project - •A valid LLM API key configured in your environment
- •Basic familiarity with agents, tasks, and crews in CrewAI
- •A working
tsconfig.json - •A
.envfile for secrets
Install the dependencies:
npm install crewai dotenv
npm install -D typescript tsx @types/node
Set your environment variables:
OPENAI_API_KEY=your_key_here
Step-by-Step
- •Start with a small routing layer. The key idea is to classify the input before you hand it to the rest of the crew. That keeps routing logic deterministic and easy to test.
export type Route = "billing" | "claims" | "general";
export function routeRequest(input: string): Route {
const text = input.toLowerCase();
if (text.includes("invoice") || text.includes("payment") || text.includes("refund")) {
return "billing";
}
if (text.includes("claim") || text.includes("incident") || text.includes("damage")) {
return "claims";
}
return "general";
}
- •Define agents for each branch. In production, keep these roles narrow so each agent has one job and one set of instructions.
import { Agent } from "crewai";
export const billingAgent = new Agent({
name: "Billing Specialist",
role: "Handles invoices, payments, refunds, and account charges",
goal: "Resolve billing-related customer requests accurately",
backstory: "You are a finance operations specialist at an insurance company.",
});
export const claimsAgent = new Agent({
name: "Claims Specialist",
role: "Handles claim intake, incident review, and claim status questions",
goal: "Triage claims requests and produce a clear next action",
backstory: "You work in claims operations and know escalation paths.",
});
export const generalAgent = new Agent({
name: "Customer Support Specialist",
role: "Handles general policy and support questions",
goal: "Answer general questions or direct them to the right team",
backstory: "You are the first-line support agent for policyholders.",
});
- •Create task builders instead of static tasks. This is where conditional routing becomes useful: you generate the task list after you know which route was selected.
import { Task } from "crewai";
import { Route } from "./router";
import { billingAgent, claimsAgent, generalAgent } from "./agents";
export function buildTasks(route: Route, input: string): Task[] {
if (route === "billing") {
return [
new Task({
description: `Investigate this billing issue and propose the next action:\n${input}`,
expectedOutput: "A concise billing resolution plan.",
agent: billingAgent,
}),
];
}
if (route === "claims") {
return [
new Task({
description: `Triage this claims request and identify required follow-up:\n${input}`,
expectedOutput: "A claims triage summary with next steps.",
agent: claimsAgent,
}),
];
}
return [
new Task({
description: `Answer this general support question clearly:\n${input}`,
expectedOutput: "A short support response with any needed escalation note.",
agent: generalAgent,
}),
];
}
- •Wire routing into a crew runner. This is the orchestration layer that turns one user input into one selected path.
import { Crew } from "crewai";
import { routeRequest } from "./router";
import { buildTasks } from "./tasks";
export async function runConditionalCrew(input: string) {
const route = routeRequest(input);
const tasks = buildTasks(route, input);
const crew = new Crew({
agents: tasks.map((task) => task.agent),
tasks,
verbose: true,
});
const result = await crew.kickoff();
return { route, result };
}
- •Add a runnable entry point. Keep it simple so you can test routing behavior from the command line before plugging it into an API or queue worker.
import dotenv from "dotenv";
import { runConditionalCrew } from "./crew-runner";
dotenv.config();
async function main() {
const input =
process.argv.slice(2).join(" ") ||
"Customer says their refund has not arrived after payment reversal.";
const output = await runConditionalCrew(input);
console.log("\nROUTE:", output.route);
console.log("\nRESULT:\n", output.result);
}
main().catch((error) => {
console.error(error);
process.exit(1);
});
Testing It
Run three inputs that should hit different branches. For example, try one message about an invoice, one about a claim incident, and one generic policy question.
Watch the printed ROUTE value first. If routing is correct but output quality is weak, your problem is in the agent instructions or task prompt, not in the router.
A good test set looks like this:
- •
"I need help with a refund on my last invoice" - •
"My car was damaged yesterday; I want to file a claim" - •
"What does my policy cover?"
If you want stronger guarantees, add unit tests for routeRequest() before you test CrewAI itself. That gives you deterministic coverage for the branching logic and keeps LLM behavior out of your routing tests.
Next Steps
- •Replace keyword routing with an LLM-based classifier when inputs get messy or domain language varies.
- •Add fallback routes for low-confidence classification and explicit human escalation.
- •Extend this pattern into multi-step branches where each route has its own sub-crew and verification task.
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
By Cyprian Aarons, AI Consultant at Topiax.
Want the complete 8-step roadmap?
Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.
Get the Starter Kit