A customer asks a question on your web widget at 2 PM, then calls at 6 PM to actually book. If those two interactions come from two different systems that don't talk to each other, that customer spends the first thirty seconds of their call re-explaining something they already told you hours earlier. We built AIVA specifically so that doesn't happen — not as a nice-to-have integration between products, but because there's only ever been one product underneath.
Why three channels doesn't mean three bots
The obvious way to build a multi-channel AI agent is one system per channel — a voice bot, an SMS bot, a chat bot — each tuned for its medium, each with its own way of remembering a conversation. It's also, we think, the wrong architecture, because it treats the channel as the thing that defines the conversation, when the channel is really just the pipe a customer happened to pick up that day. What actually defines the conversation is the customer and the business — the channel is incidental.
AIVA has one conversation engine underneath everything: the understanding, the memory, the connection to a business's booking system and FAQs, the decision-making about what to do next. The principle isn't unique to us — Twilio's own Conversations API is built around the same idea, treating the channel as a delivery detail rather than the definition of a conversation. Voice, SMS, and the web widget are thin adapters on top of that — each one translates a channel's specific format (audio in and out, a text webhook, a socket message) into and out of the same shared core. The adapters differ. What's underneath them doesn't.
What an adapter actually does — and doesn't do
It's worth being precise about where the line sits, because "thin adapter" can sound like it's doing less work than it actually is. Each adapter owns everything specific to its medium: the voice adapter handles real-time audio streaming and has to solve barge-in and interruption handling inside a tight latency budget; the SMS adapter has to hold a thread open across gaps that can run hours or days, since a text conversation has no persistent connection to lean on the way a phone call does; the web widget adapter has to render inside someone else's website without breaking their layout, and decide when it's actually useful to appear in the first place. None of that work is trivial, and none of it happens twice — it happens once, per channel, at the adapter layer.
What an adapter does not do is decide what a business's hours are, whether Thursday at 2 PM is actually free, or what a customer already told AIVA an hour ago on a different channel. That decision-making lives entirely in the shared core. An adapter's job ends at translating a channel's native format into a shape the core understands, and translating the core's response back into whatever that channel needs — audio, a text message, a widget bubble.
What that looks like in practice
A booking made over a phone call shows up correctly if the customer later texts asking "what time was that again" — because it's not a different system trying to guess at a call log, it's the same system that took the booking checking its own record. A question answered on the web widget at lunch is already known if the same customer calls that evening, so AIVA doesn't ask them to repeat it — and can pick the thread back up naturally, the way a person who'd taken the earlier message would.
Customers don't experience AIVA as three products that happen to share a login. They experience one business that remembers them, however they got in touch. That's only possible if there's actually one system underneath — not three, coordinating.
The shape of the system
In practice this means context lives in one place per customer, per business — not per channel. Every adapter, regardless of which channel it's translating, reads from and writes to that same record. When the web widget adapter learns something, it's immediately available to the voice adapter and the SMS adapter, because they're not separate memories being kept in sync after the fact. They were never separate to begin with.
This is also why building SMS commands, confirmation and reminder flows, or widget triggers never required re-solving "does this channel know what happened on the other channels." That question was answered once, at the architecture level, rather than answered three times, slightly differently, per channel.
It also shows up somewhere less obvious: the analytics dashboard. Because every channel writes to the same record, a business looking at its conversation history isn't stitching together three separate reports to understand a single customer's journey — a booking that started as a widget question and finished as a phone call appears as one continuous thread, not two disconnected entries a business owner has to manually connect. Intent breakdowns and resolution rates roll up the same way, across channels, because the underlying data was never split apart in the first place.
The case that's actually hard: one phone number, more than one person
It's worth being honest about where this model gets genuinely difficult rather than pretending it's solved everywhere. Context lives per customer, and AIVA identifies a customer largely by contact details — a phone number, most commonly. That works cleanly for the overwhelming majority of real bookings. It gets harder for a shared number: a family landline used by three people, or a small business's single line handling requests from more than one employee. Two different people calling from the same number don't automatically look like two different customers to a system built around "one record per contact." We think this is a real, open edge case rather than a solved one, and it's a large part of why AIVA still confirms specifics — a name, a service, a date — inside the conversation itself rather than assuming a phone number alone is a complete identity. A shared line is one of the places the model's simplicity costs something, and it's more honest to say so than to imply every case is seamless.
Why we think this was the right call early
It would have been faster, in the first few months, to ship three separate tools — a voice product, a chat product, an SMS product — and stitch them together later if customers asked. We think that later stitching rarely actually happens well in practice; it tends to stay bolted-together, with each channel a step behind the others and context that only sometimes makes it across. Building one core with channel adapters from the start cost more upfront and meant every new channel had to fit an existing shape rather than be whatever was fastest to ship. It also means a customer never has to think about which version of AIVA they're talking to. There's only the one.
What building it this way actually cost us
The honest trade-off is that a shared core is slower to extend than three independent products would be in the short term. Adding a genuinely new capability means it has to make sense across every channel it will eventually reach, not just the one channel someone wants it for today — a feature that only makes sense on voice, say, still has to be designed with an eye toward whether SMS or the widget would ever need a version of it, even if the answer is no for now. That's more upfront thinking than "just ship it on the channel that asked for it." We think that discipline is why voice, SMS, and the widget still feel like the same product three channels in, rather than three products slowly drifting apart the longer each one exists.
If you want to see what one system across every channel actually feels like, try AIVA's voice agent, then follow up by texting the same number — or start free and watch a single conversation follow you across both.