CrewAI Tutorial (TypeScript): building conditional routing for intermediate developers

By Cyprian AaronsUpdated 2026-04-21
crewaibuilding-conditional-routing-for-intermediate-developerstypescript

This tutorial shows you how to build a CrewAI workflow in TypeScript that routes tasks conditionally based on the input and intermediate results. You need this when a single linear agent flow is too rigid and you want the system to branch into different specialists depending on whether the request is sales, support, or something that needs escalation.

What You'll Need

  • Node.js 18+ installed
  • A TypeScript project with tsconfig.json
  • crewai installed
  • An LLM provider API key, such as:
    • OPENAI_API_KEY
  • Basic familiarity with:
    • agents
    • tasks
    • crews
    • async/await in TypeScript

Install the package:

npm install crewai
npm install -D typescript ts-node @types/node

Step-by-Step

  1. Start by defining a small routing contract. The router should return a simple label, not a full answer, because its job is to decide where the request goes next.
import { Agent, Task, Crew } from "crewai";

type Route = "billing" | "technical" | "escalation";

const routerAgent = new Agent({
  name: "Router",
  role: "Request Router",
  goal: "Classify incoming requests into billing, technical, or escalation",
  backstory: "You are precise and conservative when choosing routes.",
});

const routeTask = new Task({
  description:
    "Classify this customer request into one of: billing, technical, escalation.",
  expectedOutput: "A single label only.",
  agent: routerAgent,
});
  1. Create specialist agents for each branch. Keep them narrow so the routing decision has real value; otherwise conditional routing just adds complexity without improving output.
const billingAgent = new Agent({
  name: "BillingAgent",
  role: "Billing Specialist",
  goal: "Handle billing-related customer requests",
});

const technicalAgent = new Agent({
  name: "TechnicalAgent",
  role: "Technical Support Specialist",
  goal: "Handle technical troubleshooting requests",
});

const escalationAgent = new Agent({
  name: "EscalationAgent",
  role: "Escalation Handler",
  goal: "Summarize high-risk requests for human review",
});
  1. Build a function that runs the router first, then dispatches to the right specialist based on the result. This is the core pattern: one lightweight classification step followed by a conditional branch.
async function routeRequest(input: string): Promise<Route> {
  const crew = new Crew({
    agents: [routerAgent],
    tasks: [routeTask],
    verbose: true,
  });

  const result = await crew.kickoff({ input });
  const label = String(result).toLowerCase();

  if (label.includes("billing")) return "billing";
  if (label.includes("technical")) return "technical";
  return "escalation";
}
  1. Add execution paths for each route. Each branch gets its own task and crew so you can tune prompts independently and keep outputs predictable.
async function handleBilling(input: string) {
  const task = new Task({
    description: `Resolve this billing issue clearly and concisely:\n${input}`,
    expectedOutput: "A customer-ready billing response.",
    agent: billingAgent,
  });

  return new Crew({ agents: [billingAgent], tasks: [task] }).kickoff();
}

async function handleTechnical(input: string) {
  const task = new Task({
    description: `Troubleshoot this technical issue step by step:\n${input}`,
    expectedOutput: "A practical troubleshooting response.",
    agent: technicalAgent,
  });

  return new Crew({ agents: [technicalAgent], tasks: [task] }).kickoff();
}
  1. Finish with an orchestrator that combines routing and execution. This keeps your app code clean and makes it easy to add more branches later without rewriting the flow.
async function handleEscalation(input: string) {
  const task = new Task({
    description: `Summarize this case for human escalation:\n${input}`,
    expectedOutput:
      "A short escalation summary with risk factors and next action.",
    agent: escalationAgent,
  });

  return new Crew({ agents: [escalationAgent], tasks: [task] }).kickoff();
}

export async function processRequest(input: string) {
  const route = await routeRequest(input);

  switch (route) {
    case "billing":
      return handleBilling(input);
    case "technical":
      return handleTechnical(input);
    default:
      return handleEscalation(input);
  }
}
  1. Run it with a few test inputs to confirm the branches behave differently. You want obvious separation between routes so you can tell whether the router is making useful decisions.
async function main() {
  const samples = [
    "I was charged twice for my subscription.",
    "My app crashes when I upload a document.",
    "This account might be compromised; please escalate immediately.",
  ];

   for (const sample of samples) {
    const output = await processRequest(sample);
    console.log("\nINPUT:", sample);
    console.log("OUTPUT:", String(output));
   }
}

main().catch(console.error);

Testing It

Run the script with npx ts-node your-file.ts and verify that each sample lands in a different branch. The billing input should produce a response focused on charges or refunds, while the technical input should produce troubleshooting steps.

The escalation case should not try to solve everything; it should summarize risk and handoff details instead. If every input produces similar output, your router prompt is too vague or your labels are too broad.

Turn on verbose during development so you can inspect how the router behaves before dispatching. In production, log only the chosen route and final branch output.

Next Steps

  • Add confidence thresholds so low-confidence classifications always go to escalation
  • Replace string matching with structured JSON routing output from the router agent
  • Add more branches like fraud, retention, or compliance review

Keep learning

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

Related Guides