How We Gave InAgentic a Phone Number That Actually Understands You
We just added a voice channel to InAgentic: call our number, and instead of a menu of button presses, you're talking to the same AI that runs our chat — it can book you an appointment or write you something and text it to your WhatsApp before you've hung up. Here's how it's built, and why we picked
We just added a voice channel to InAgentic: call our number, and instead of a menu of button presses, you're talking to the same AI that runs our chat — it can book you an appointment or write you something and text it to your WhatsApp before you've hung up. Here's how it's built, and why we picked the tool we did.
Vapi vs. building it ourselves
There are, broadly, four ways to put an AI on the other end of a phone call:
Roll your own pipeline. Twilio Media Streams gives you the raw audio; you wire up speech-to-text, an LLM, and text-to-speech yourself, and handle turn-taking, interruptions, and latency by hand. Total control, but it's weeks of work before you're even at "it works," and most of that work is plumbing you'll never touch again once it's running.
Twilio's own voice AI (ConversationRelay). Native to the number we already have, but younger and noticeably more rigid on tool-calling and voice provider choice than the dedicated platforms.
Self-hosted agent frameworks like LiveKit Agents or Pipecat. Open source, and you can bend them into any shape — at the cost of owning the infrastructure and the on-call pager that comes with it.
Managed voice agent platforms — Vapi, Retell, Bland, and a handful of others. They own the hard, boring parts (turn-taking, interruption handling, streaming STT/TTS) and hand you a clean surface: a system prompt, a set of tools, and a webhook.
We went with the fourth option, and Vapi specifically, for three reasons that mattered more than the usual feature checklist:
- It imports an existing Twilio number. We already had a number doing WhatsApp Business messaging for our chat-overseer notifications. Vapi's "import Twilio number" flow took over just the voice routing — no new number, no migration, nothing else about that number changed.
- It supports Claude via AWS Bedrock. Every other AI feature on this site — chat widgets, booking flows, content generation — runs on the same Claude Sonnet model through our own AWS account. Vapi's Bedrock support meant the phone assistant's brain could be the same model, not a second vendor relationship to manage.
- The whole setup is API-driven. No dashboard to click through by hand and hope someone remembers how, next time. The assistant config, its tools, and the phone number import all live in one script we can re-run.
Reusing what already existed
The interesting part of this build wasn't the phone call — it was almost entirely not reimplementing things we already had:
- Booking: the phone assistant's
check_availabilityandbook_appointmenttools call directly into the same Google Calendar check and pending-booking flow that already powers the site's chat-based booking (the same one behind dogrun.ai's booking chat and FileDone's compliance callback flow). Same calendar, same "request goes out, we confirm by email" pattern — a caller booking time by voice ends up in exactly the same queue as someone booking through the website. - The Twilio number: already ours, already voice-capable, just pointed at Twilio's demo greeting and doing nothing. Vapi picked up voice routing on it without touching the WhatsApp side at all.
- The LLM: the same Bedrock-hosted Claude Sonnet model as every other agent on the site, via a narrowly-scoped IAM role that lets Vapi assume it through AWS STS — no static keys handed to a third party, and the permissions only cover invoking that one model.
What's actually new: content by phone, delivered on WhatsApp
The one genuinely new capability: ask the assistant to write something, and it'll text it to you.
Say "write me something about X," and the assistant asks for a PIN — a short code that maps to a name and a WhatsApp number on file. Once verified, it generates the content and sends a WhatsApp message with a link to it. Say "change it, make it shorter" on the same call, and it revises that same draft and sends an updated link — it doesn't start over as a new, unrelated request.
Building this surfaced a genuinely non-obvious WhatsApp rule: a business can only send a free-form WhatsApp message to someone who has messaged it first, within the last 24 hours. A phone call never does that — so every message this feature sends, including revisions, has to go through a pre-approved message template, not just the first one. That template also can't hold a full article's worth of text, which turned out to be the right constraint anyway: we deliver a short WhatsApp notice with a link, rather than trying to stuff an entire draft into a text message.
The plumbing
One webhook route on our backend receives every tool call Vapi makes during a conversation and dispatches it by name — availability checks, bookings, PIN lookups, content generation, WhatsApp sends. State for an in-progress call (which PIN was verified, which draft is being worked on) is kept in memory, keyed by the call ID Vapi already gives us — no separate session system to invent or keep in sync.
Wiring Claude into Vapi via Bedrock took a bit of trial and error against Vapi's own API schema — tools turned out to belong nested inside the model configuration rather than at the top level, each tool needs an explicit type: "function" field, and the model name is a plain version string rather than the full Bedrock inference profile ID we use everywhere else internally. Small details, but the kind that only show up by actually reading the API spec rather than guessing from the docs page.
Try it
The number's live. Call it, ask it to book you time with us, or ask it to draft something and text it to your WhatsApp — and if you don't like the first draft, just say so.