| name | zernio-voice-agent |
| description | Give ONE WhatsApp number a full AI front desk โ text it and an AI replies (Zernio workflow with memory); CALL it and an AI voice picks up (Zernio WhatsApp Calling routes inbound calls via forwardTo to a Retell AI voice agent). Claude wires the whole thing: the Retell agent + voice prompt, the calling config on the number, and the text workflow with a tap-to-call escalation. Live-proven inbound-first; also covers the outbound start_call node for when Meta grants outbound. Triggers on: "AI answers my WhatsApp calls", "WhatsApp voice agent", "AI receptionist on WhatsApp", "connect Retell to Zernio", "route my WhatsApp calls to an AI", "AI front desk". |
zernio-voice-agent
One WhatsApp number, full AI front desk. Text it โ an AI answers in chat. Call it โ an AI voice
picks up. Two tools, one bridge:
- Zernio owns the WhatsApp number: the text workflow (chat brain) and the call routing.
- Retell AI is the voice: real-time listen โ think โ talk, human-sounding.
- The bridge is one field: the number's
forwardTo โ your Retell agent's SIP/wss endpoint.
The skill informs; the agent acts. This file + reference/ give Claude the verified
contract. You stay in control โ Claude shows you both prompts, the graph, and the calling
config before creating or activating anything (it goes live on a real business number).
The architecture (live-proven shape)
ONE WhatsApp NUMBER (Zernio, calling-eligible)
/ \
TEXT: inbound message CALL: inbound WhatsApp call
โ โ
Zernio workflow: tag โ human-escape โ ai (memory) โ reply โ wait โ number's calling config:
โ contact asks to TALK โ forwardTo: sip:<retell>@sip.retellai.com
โโโถ send TAP-TO-CALL deep link (wa.me/call/<number>) โโโโโโโโโค
โผ
Retell voice agent ANSWERS
(same persona as the text agent)
Why tap-to-call instead of the outbound start_call node: outbound calling is gated
separately by Meta (outboundDisabled stays true on most numbers). The deep link needs only
INBOUND calling, works the moment forwardTo is set, and converts better anyway โ the contact
chooses to call. Keep start_call for when outbound unlocks.
What Claude needs (ask once, remember in-session)
- ZERNIO_API_KEY + RETELL_API_KEY (Retell needs a card on file to provision a number).
- profileId + accountId of the WhatsApp account (
GET /v1/profiles, GET /v1/accounts).
- A calling-eligible WhatsApp number โ see the gates below. This is the long pole.
- The business details in plain words โ for BOTH prompts (text + voice), same persona.
The three gates (check BEFORE building โ each returns a specific 4xx)
- Retell card on file โ
create-phone-number returns 402 without payment.
- Zernio usage-based plan โ calling is metered; flat plans return 422
"available on usage-based billing only". Only the account owner can switch plans.
- Meta messaging tier โ a fresh number (TIER_250) cannot call; enabling returns 422
"requires a daily messaging limit of at least 2,000". The tier is EARNED: message ~2ร your
current limit in unique opted-in recipients over 7 days with good quality (250โ1Kโ10K...).
No API, payment, or support ticket bypasses this โ use a warmed number or budget weeks.
The recipe (Claude follows this)
1. Build the voice agent (Retell)
Prompt from templates/retell-voice-agent.base.md (voice discipline: short turns, one question,
confirm names/numbers back, no lists/URLs read aloud). Create the LLM + agent:
POST /create-retell-llm โ POST /create-agent (response_engine: {type: retell-llm, llm_id},
voice_id). Test in the Retell playground before wiring anything.
2. Give the agent an inbound line (Retell)
POST /create-phone-number with inbound_agents: [{agent_id, weight: 1}]
(the old inbound_agent_id field is deprecated โ returns 400). This number is the forward target.
3. Enable calling on the WhatsApp number (Zernio โ THE bridge)
curl -s -X POST "https://zernio.com/api/v1/whatsapp/phone-numbers/{phoneNumberDocId}/calling" \
-H "Authorization: Bearer $ZERNIO_API_KEY" -H "Content-Type: application/json" \
-d '{"accountId":"<accountId>","forwardTo":"tel:+1<retellNumber>","recordingEnabled":false}'
Use tel: for a Retell-PURCHASED number โ it's the proven path. The purchased number is a
real, managed phone number; forwarding by plain call needs zero extra setup. The
sip:+1<num>@sip.retellai.com;transport=tcp form is the advanced option: it skips the PSTN leg
(slightly cheaper/faster) but Retell's direct-SIP ingress is designed for its elastic SIP
trunking (imported numbers, IP-allowlist on the SIP-sender's side โ which is the platform's
infra, not yours). Verify a sip: forward with a live test call before trusting it; if the call
doesn't land, fall back to tel:.
Verify with GET /v1/whatsapp/calling?accountId=<accountId> โ expect callingEnabled: true and a
callDeepLink (https://wa.me/call/<number>) โ save that link for step 4.
Full endpoint set + field semantics: reference/whatsapp-calling-api.md.
4. Build the text agent (Zernio workflow)
Start from templates/whatsapp-ai-front-desk.workflow.json โ the live-tested graph: memory
loop + human-escape + call-intent detection โ sends the callDeepLink. Fill the [BRACKETS],
paste the deep link, POST /v1/workflows, confirm with the user, /activate.
5. Test end to end (in this order)
Text โ short reply with memory โ "can we talk on a call?" โ deep link arrives โ tap โ the voice
agent answers โ give a name + number โ it confirms them back โ hang up โ chat continues โ
"let me talk to a REAL person" โ handoff fires. Inspect runs: GET /v1/workflows/{id}/executions.
Gaps to design around (all live-verified)
- Condition regex needs
operator: "matches" โ with contains the regex is treated as a
literal substring and the rule silently NEVER fires (everything falls to the AI). This bug ships
easily and looks like "the AI just talks a lot."
- One persona across text + voice โ same name, same warmth in both prompts, or the chatโcall
hop feels like a transfer. The voice agent must NOT pretend to remember chat specifics (the two
brains share no history) โ instruct it to "continue helping naturally."
- Keep text replies SHORT โ hard-rule the style block (max 1โ2 sentences, one question). Long
AI replies read as spam on WhatsApp and hurt the quality rating your tier depends on.
- Memory accumulator on the text AI node (
set_variable history trick) โ without it the agent
re-greets every message. Unbounded growth caveat applies (see zernio-workflow-creator).
outboundDisabled is separate from inbound โ inbound calling live โ start_call works.
Wire outbound only after checking the flag.
- Recording off by default โ enable only with consent + disclosure (two-party-consent laws).
- Two bills โ Zernio meters the WhatsApp/carrier side; Retell bills voice minutes + the
number's monthly fee. Cents per call, but know it.
- Both APIs move fast โ re-verify against live docs before a production deploy.
Guardrails
- Confirm before create/activate/enable-calling. Show both prompts, the graph, the forwardTo.
- Never print API keys.
- Workflow edits only while paused/draft โ pause โ PATCH โ activate.
- The voice IS the brand on the phone โ pick it deliberately, test by talking, not reading.
Reference
reference/whatsapp-calling-api.md โ Zernio WhatsApp Calling: all four calling endpoints,
forwardTo forms, gates, deep link, start_call node spec.
reference/retell-voice-api.md โ Retell wiring: agent/LLM creation, inbound_agents, SIP forms.
templates/whatsapp-ai-front-desk.workflow.json โ the proven inbound graph (text + tap-to-call).
templates/whatsapp-voice-agent.workflow.json โ the outbound start_call variant (for when
Meta grants outbound).
templates/retell-voice-agent.base.md / .example.md โ the voice prompt (blank + filled).
- Pairs with
zernio-workflow-creator (full 16-node Workflow API contract) and
zernio-comment-to-dm (comment โ DM โ this front desk).