Skip to content

Factory agents

Pull a ready-made voice agent into your codebase and ship it in minutes.

View .md

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
terminal
npx zavudev login npx zavudev whoami # confirm the project

2. Browse the catalog

terminal
npx zavudev agents catalog
Code
id name voice tools category fermi Fermi — Lead Qualification yes 1 sales support Support — Customer Support yes 2 support

3. Find your sender

terminal
npx zavudev senders list export SENDER_ID="jn76vnxet8g5nq661by3v06y1581bmmn"

No sender yet? Create one — a phone number gives you voice and SMS right away:

terminal
npx 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

terminal
npx zavudev agents pull fermi --sender "$SENDER_ID" cd fermi

This scaffolds an editable index.ts:

TypeScript
import { 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

terminal
npx 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:

terminal
npx zavudev calls create --to "+15551234567" --sender "$SENDER_ID"

7. Watch it run

terminal
npx 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:

terminal
npx 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.

Next steps