Factory agents
Pull a ready-made voice agent into your codebase and ship it in minutes.
Factory agents
Factory agents are ready-made agents you pull straight into your project with
one command, then deploy. Under the hood each is a Zavu Function
that declares an agent with defineAgent and its skills with defineTool — so
what you pull is real, editable code you own, not a black box.
npx zavudev agents pull fermi scaffolds the agent, registers it, and leaves you a
directory to npx zavudev deploy. Voice agents answer phone calls; text agents run on
WhatsApp, SMS, and more.
1. Install the CLI
# No install step. Always runs the current version.
npx zavudev@latest --version
terminalnpx zavudev login npx zavudev whoami # confirm the project
2. Browse the catalog
terminalnpx zavudev agents catalog
Codeid name voice tools category fermi Fermi — Lead Qualification yes 1 sales support Support — Customer Support yes 2 support
3. Find your sender
terminalnpx zavudev senders list export SENDER_ID="jn76vnxet8g5nq661by3v06y1581bmmn"
No sender yet? Create one — a phone number gives you voice and SMS right away:
terminalnpx zavudev phone-numbers search --country US npx zavudev phone-numbers buy +1XXXXXXXXXX npx zavudev senders create --name "My agent" --phone +1XXXXXXXXXX --set-default
Connect more channels (from the CLI)
Voice and SMS work as soon as the sender has a number. The rich channels each connect with one command:
terminal# Telegram — paste a bot token from @BotFather npx zavudev telegram connect --sender "$SENDER_ID" --token <botfather-token> # Email — add a domain, publish the DNS records it prints, then verify npx zavudev email-domains add example.com npx zavudev email-domains verify <domainId>
The agent answers on the channels its own channels list names, and only those.
Connecting a channel to the sender is necessary and not sufficient: a channel the
sender receives on but the agent does not list is dropped before the agent sees
it, with no error and no reply. It is recorded: the drop lands in
GET /v1/senders/{senderId}/agent/executions with status: "filtered" and a
line saying which channel arrived and which the agent listens on, and the
dashboard's Agents → Logs → Filtered view shows the same. The factory agents ship channels: ["*"]
— every channel their sender carries — so this only bites once you narrow it.
Check the channels line in the file you pulled before you go looking for a bug
in Telegram.
4. Pull the agent
terminalnpx zavudev agents pull fermi --sender "$SENDER_ID" cd fermi
This scaffolds an editable index.ts:
TypeScriptimport { defineAgent, defineTool } from "@zavudev/functions" defineAgent({ senderId: process.env.SENDER_ID!, name: "Fermi", provider: "zavu", model: "openai/gpt-5.6-luna", channels: ["voice", "whatsapp"], voice: { enabled: true, model: "openai/gpt-4o", // co-located voice model — lowest latency greeting: "Hi, I'm Fermi. Tell me a bit about what you want to build.", language: "en", interruptible: true, maxCallDurationMinutes: 10, }, prompt: `# Personality You are Fermi, a sharp, friendly lead-qualification agent…`, }) defineTool({ name: "qualify_lead", description: "Record a qualified lead once you know the use case, channels, and volume.", parameters: { type: "object", properties: { /* … */ }, required: ["use_case", "monthly_volume", "score"] }, handler: async (args) => { // TODO: push this lead to your CRM / Slack / webhook. return { ok: true } }, })
5. Deploy
terminalnpx zavudev deploy
Code✓ Deployed in 12s Agents synced: + Fermi Tools synced: + qualify_lead
Your agent is live. For a voice agent, the sender's number now answers calls with Fermi; for a text agent, every inbound message hands off to it.
6. Call it
Dial the sender's phone number from your phone. Fermi greets you, qualifies the
lead, and calls qualify_lead near the end of the conversation.
Prefer to test outbound? Place a call from code:
terminalnpx zavudev calls create --to "+15551234567" --sender "$SENDER_ID"
7. Watch it run
terminalnpx zavudev fn logs --tail # live tool calls (your handler activity) npx zavudev agents executions --sender "$SENDER_ID" # tools used, tokens, cost, latency npx zavudev calls list --limit 10 # every call in & out
8. Iterate
Edit index.ts — tweak the prompt, add a defineTool, change the greeting —
and redeploy:
terminalnpx zavudev deploy
The summary shows only what changed. There's no separate agent config to keep in sync: the code is the source of truth.
Common pitfalls
The agent doesn't answer calls
Confirm the sender has a phone number assigned.
Then check the deploy printed Agents synced: + Fermi and that the voice
block has enabled: true.
`voice config ignored` warning on deploy
A field in your voice block is out of range (e.g.
maxCallDurationMinutes above the cap) or misspelled. The deploy warns and
ships the agent as text-only rather than failing. Fix the value and
redeploy.
A skill never gets called
The LLM decides when to call a tool from its description. Rewrite each
description as the answer to "when should the model call this?" — vague
descriptions don't trigger.