We Open Sourced an AI That Makes Phone Calls. We Named It After the Dog.
hatch

We just open sourced an AI that places real phone calls on your behalf. We named it after the dog.
Quick roadmap:
- Meet the actual Banjo, and why the name isn’t just a cute mascot choice
- What the software does, in one real example
- Five ways the dog’s actual personality shows up in the architecture
- Two bugs that made it to a live phone call before we caught them
- What’s still held together with tape, said plainly
- How to run it yourself
Meet Banjo
Banjo is a rescue terrier mix. Nobody in the family agreed on his exact breed mix and everyone agreed on the verdict: the little terrier with the big personality. He has ears that stand up like he’s perpetually mid-question and a face that looks skeptical of your plans by default. There’s a photo of him in a pirate costume on the beach in Asbury Park, standing his ground against wind that’s actively trying to remove his hat. He did not ask for the costume. He tolerated it, the way he tolerates most indignities, with visible reservations and zero actual protest.
That’s the dog the open-source AI executive assistant is named after. Not because dogs and AI agents make a cute pairing on a landing page. Because once I’d built the thing, the personality mapped on with almost no effort.
He’s animated too, blink and all, mid-call to Claudia’s:
What it actually does
Banjo places real outbound phone calls to get things done. Book a haircut. Make a dinner reservation. Call and leave a message with the plumber. It’s the phone-calling half of a two-part system. The other half is a Claude Code skill that decides whether a task can be booked online or needs an actual human on the other end of a phone line, and only calls into Banjo when a real call is the right move.
Say the request into Claude: “Schedule a haircut with Claudia’s.” (Yes, that’s a real salon name, and yes, the irony of Claude booking me a haircut at a place called Claudia’s is not lost on me.) Claude checks whether that salon books online, and whether I’ve already told it I prefer calling that particular business. If it’s a phone job, Claude hits place_call(). Banjo creates a task, acknowledges it immediately, and tells me it’s started. Then, asynchronously: it checks my calendar for open windows, dials the salon through Twilio, and negotiates the appointment live with whoever picks up. It checks alternate times against my calendar mid-call if the offered slot doesn’t fit. It leaves a voicemail if nobody answers. It writes the confirmed appointment to my Google Calendar the moment it’s locked in, using an idempotency key generated on the server, not one the model made up, so a dropped call after a successful booking can’t double-book me. Either way, I get a text telling me what happened.
Here’s the whole thing end to end, the Voice AI and telephony layers each swappable on their own, the one component in the middle that touches both:
There’s also an interactive, explorable version if you’d rather click through the call flow than stare at a flat image.
The dog is the architecture
I didn’t design the system around the dog. I noticed the overlap after the fact, and it was too clean to leave out.
One dog, one household. Banjo the dog belongs to one family. Banjo the service is single-tenant by design: you run your own instance against your own Twilio number, your own calendar, your own voice AI credentials. There’s no fleet mode, no multi-customer dashboard. Fork it and make it yours, the same way you don’t share a dog with the neighbors.
He does not let go of a hole. Terriers were bred to go down after something and stay down there. Banjo’s call-booking flow is a state machine, pending through checking availability, calling, negotiating, and out the other side to confirmed, voicemail left, or escalated. A background poller picks up any task that’s stuck mid-flow and keeps working it. Kill the process mid-call, restart it, and the task picks back up where it left off instead of vanishing. That’s not a metaphor I imposed. That’s what the retry logic does, and it happens to be exactly the trait.
Independent worker, minimal handler input. A terrier that needs a command for every step isn’t doing its job. Banjo can finalize a booking on the call itself, live, no check-in with me mid-negotiation, as long as the time fits what I asked for upfront. The autonomy has a boundary. It doesn’t need a leash for every decision inside that boundary.
Goes out, does the thing, comes back with news. Every call ends the same way from my side: a text telling me what happened. Confirmed, voicemail left, couldn’t get through, needs me to step in. That’s fetch, structurally. Go get the thing, bring back proof.
Scrappy build, not one polished pedigree. He’s a mutt. The system underneath Banjo is assembled the same way: three swappable voice AI providers, Twilio for the phone leg, Postgres for the source of truth, none of it locked to a single vendor’s black box. It’s the version you can actually read, run yourself, and change, which is a different animal from a purebred SaaS platform that hides its insides on purpose.
What broke on a real phone call
Two bugs made it all the way to an actual ringing phone before I found them, and both are worth saying out loud instead of quietly patching and moving on.
The first landed a real appointment four hours off. I asked for 2pm. The calendar event came in at 10am. The cause was almost embarrassingly simple: a timestamp like 2026-08-04T14:00:00 has no timezone attached to it, and whatever was reading it assumed UTC. Nothing in the code had ever been told which timezone “2pm” was supposed to mean. The fix was to stop letting a bare timestamp exist anywhere on a call-facing or calendar-writing path. Every date and time now goes through one converter that knows the configured local timezone and handles daylight saving correctly, and there’s exactly one setting anywhere in the system that defines what “2pm” means when someone says it out loud.
The second one is worse in a quieter way. The tool that leaves a voicemail took the message as an argument, and the model would say a short preamble out loud, then pass the actual message into the tool call instead of speaking it. The callee heard a sentence and a half. The database and the outcome text both recorded a full message as delivered. Twice, on real calls, someone got a truncated voicemail while every system of record insisted they’d gotten the whole thing. The fix: the exact message now gets force-spoken by the voice AI itself, word for word, before the hang-up logic runs at all. What got recorded and what got said can no longer disagree with each other, because they’re now the same event.
What’s still held together with tape
Only one of the three voice AI providers has ever carried a real phone call. OpenAI’s Realtime API is the one doing actual work right now. The other two are wired up, pass their tests, and have never once talked to a real human on a real line. I’m not going to pretend otherwise in the code comments or here.
There was also a second telephony backend, built early alongside Twilio, that never got past placeholder audio and never carried a single real call. Once that was clear, it got deleted outright instead of sitting in the repo as dead weight nobody was going to fix. A terrier doesn’t guard a hole once it’s empty.
Try it
Banjo is MIT licensed and built to be self-hosted. It’s not a service you sign up for. It’s code you run against your own phone number, your own calendar, your own AI provider keys, and you own all of it: github.com/shatch/banjo.
He’s not a good boy who does tricks. He’s a good boy who makes your phone calls, and now you can go see exactly how he does it.

