CrewAI Tutorial (TypeScript): adding human-in-the-loop for beginners

By Cyprian AaronsUpdated 2026-04-21
crewaiadding-human-in-the-loop-for-beginnerstypescript

This tutorial shows you how to add a human approval step to a CrewAI workflow in TypeScript, so your agent can pause before taking risky actions. You need this when an agent is about to send an email, approve a payment, or finalize a customer-facing response and you want a person to review the output first.

What You'll Need

  • Node.js 18+
  • A TypeScript project with ts-node or tsx
  • CrewAI JS/TS package installed
  • An OpenAI API key set in your environment
  • A terminal where you can run interactive prompts
  • Basic familiarity with CrewAI agents, tasks, and crews

Install the dependencies:

npm install crewai openai dotenv readline-sync
npm install -D typescript tsx @types/node @types/readline-sync

Set your API key:

export OPENAI_API_KEY="your-key-here"

Step-by-Step

  1. Start with a small CrewAI setup that produces something worth reviewing. The key idea is simple: let the agent draft the output, then stop before execution.
import "dotenv/config";
import { Agent, Task, Crew } from "crewai";

const writer = new Agent({
  name: "Policy Writer",
  role: "Insurance Policy Assistant",
  goal: "Draft a clear customer email explaining a claim status update",
  backstory: "You write concise, professional insurance communications.",
});

const task = new Task({
  description:
    "Draft an email telling the customer their claim is under review and needs one more document.",
  expectedOutput: "A professional email draft.",
  agent: writer,
});

const crew = new Crew({
  agents: [writer],
  tasks: [task],
});
  1. Run the crew and capture the draft text. For human-in-the-loop flows, do not immediately send or execute anything after generation.
async function main() {
  const result = await crew.kickoff();
  console.log("\n--- DRAFT ---\n");
  console.log(result);
}

main().catch(console.error);
  1. Add an approval gate using terminal input. This is the human-in-the-loop part: a person reads the draft and explicitly approves or rejects it.
import readlineSync from "readline-sync";

function askForApproval(draft: string): boolean {
  console.log("\nReview this draft carefully:\n");
  console.log(draft);
  const answer = readlineSync.question("\nApprove? (yes/no): ");
  return answer.trim().toLowerCase() === "yes";
}
  1. Wire the approval into the workflow so execution only continues on approval. In production, this is where you would call your email service, CRM update, or payment system.
async function main() {
  const result = await crew.kickoff();
  const approved = askForApproval(String(result));

  if (!approved) {
    console.log("Rejected by human reviewer. Nothing was sent.");
    return;
  }

  console.log("Approved. Proceeding with downstream action...");
}

main().catch(console.error);
  1. Make the output more structured so reviewers can make faster decisions. Ask the agent for JSON-like sections instead of a long free-form paragraph.
const task = new Task({
  description:
    "Draft an email with these sections: subject, summary, risks, and final message.",
  expectedOutput:
    "A structured response with subject, summary, risks, and final message.",
  agent: writer,
});
  1. Add a simple audit log so you know who approved what and when. Human-in-the-loop systems need traceability; otherwise you cannot explain why an action happened later.
import fs from "node:fs";

function logDecision(draft: string, approved: boolean) {
  const entry = {
    timestamp: new Date().toISOString(),
    approved,
    draft,
  };

  fs.appendFileSync("approval-log.jsonl", JSON.stringify(entry) + "\n");
}

Testing It

Run the script with tsx or your preferred TypeScript runner:

npx tsx src/index.ts

You should see the agent’s draft printed in the terminal, followed by an approval prompt. If you type no, nothing downstream should happen; if you type yes, the script should continue past the gate.

Test both paths deliberately:

  • Approve once and confirm the log file is written.
  • Reject once and confirm no action is taken.
  • Change the task prompt and verify the reviewer still sees readable output.

If you want to simulate production behavior, replace the final console.log with a real integration like sending an email or creating a ticket after approval.

Next Steps

  • Add role-based approvals so only certain users can approve certain actions.
  • Replace terminal input with a web UI or Slack approval button.
  • Store approval events in Postgres or your audit system instead of flat files.

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