Appearance
Legacy vs Blueprint
How Avoca builds and serves voice agents has been undergoing a significant architectural shift. As of 2026 the new pattern is in production for some clients (including the EAS Ponderosa pilot), the old pattern is still in production for others, and FDE workflows differ between the two. This page explains both, why the shift happened, and how to tell which pattern a given client is on.
Unsolicited Opinion
This is the single most important Avoca-architecture concept for an FDE to internalize. Most of the dev-environment friction — why prompt edits go through one surface for some clients and a different one for others, why local dispatch routing is hard for blueprint-managed assistants, why test phones exist at all — all rolls up to which architectural pattern the client is on. Read this before anything else in this section.
The two patterns
Legacy pattern
Shape: the agent's design (system prompt, tool definitions, hand-off rules, voice settings) lives in Vapi's cloud. The Twilio phone number is statically wired to one Vapi assistant or squad. Avoca's backend is a thin wrapper that handles tool dispatches when the LLM invokes them.
FDE workflow on a legacy client:
- Edit the system prompt in Vapi's UI directly.
- Tools defined in Vapi (server URL fields per-tool).
- Phone number → static
assistantIdin Vapi's config. - Test by calling the number; Vapi runs the assistant exactly as configured.
- Avoca's role: receive tool-call webhooks from Vapi, run handlers, return results.
Trade-offs:
- Simple to grok. What you see in Vapi is what runs.
- Single editing surface for the agent itself. Prompt + tools + voice all in Vapi.
- Tightly coupled. Avoca-side experiments / runtime-variable injection / per-team variants require sync mechanisms or duplicate Vapi assistants.
- Per-tenant config drift. Ten clients = ten Vapi assistants to keep aligned.
Current pattern
Shape: the agent's design lives in Avoca's database (assistant_configs.multi_agent_config, populated from a blueprint template). Twilio phone numbers don't have a static assistantId — they have a serverUrl pointing at Avoca. When a call comes in, Vapi asks Avoca "what assistant should I run?" and Avoca returns a dynamically-built payload. Vapi becomes a pure LLM runtime.
FDE workflow on a current-pattern client:
- Edit the squad design in Avoca admin (or a blueprint editor) — Vapi UI is read-only relative to Avoca's data.
- Tools, prompts, hand-offs all live in Avoca's database.
- Phone numbers are dumb pipes —
serverUrlonly. - Test requires a test phone + sync mechanism so the squad's sub-agent server URLs point at the FDE's tunnel (see Squads page for the priority-shadowing detail and Local Dev Setup for the FDE flow).
- Avoca's role at call time: dynamic assistant resolution + tool dispatch + post-call workflow.
Trade-offs:
- Single editing surface (Avoca admin). No two-system coordination.
- Templating via blueprints.
StraightAway Auto v11applied to N auto-shop tenants. Edit the blueprint, all tenants get the update. - Dynamic per-call decisions. A/B tests, experiments, feature flags, runtime-variable injection — possible because Avoca decides per-request, not at config time.
- Multi-platform optionality. Avoca's
serverUrlresponse could be Vapi config OR ElevenLabs config OR another vendor. Vapi isn't locked-in. - More moving parts. Phone-number config, Vapi runtime, Avoca admin, Supabase, blueprints, sub-agent serverUrls, priority levels — all need to be coherent for a call to work.
- Harder dev story. Editing requires knowing which sub-agent owns which conversational moment. Local validation requires test-phone provisioning + sync mechanism.
Telling the two apart
Look at the phone number's Vapi config
| Field | Legacy | Current |
|---|---|---|
assistantId | Set to a Vapi assistant id | Empty / not set |
squadId | Set if it's a squad | Empty / not set |
serverUrl | Sometimes set (for tool dispatch) | Always set (Vapi asks here for the assistant per call) |
If you see no assistantId AND no squadId on the phone, it's the current pattern.
Look at the voice assistant in Avoca admin
Open app.avoca.ai/admin (or your localhost equivalent) and find the team's voice assistant. Check the assistant_mode (or "type" in some UIs):
SINGLE_AGENT(or no MULTI_AGENT marker) → most likely legacy or hybridMULTI_AGENT→ current pattern, blueprint-managed squad
If a blueprint is attached (e.g., StraightAway Auto v11), it's definitely the current pattern.
Look at the codebase
In apps/web/lib/voice-assistants/agents/agent-factory.ts:
ts
case AssistantMode.MULTI_AGENT:
// build MultiAgent from multi_agent_config
case AssistantMode.SINGLE_AGENT:
// build VapiAgent (legacy-style wrapper)The factory branches on assistant_mode. The branch that constructs MultiAgent is the current-pattern path. The branch that constructs VapiAgent is the legacy path.
Why the shift happened
In short: single source of truth + multi-tenant scalability + multi-platform optionality. Once Avoca had more than a handful of similar clients (auto shops, plumbing, garage doors), maintaining N Vapi assistants in sync became untenable. Blueprints + dynamic-resolution lets one design serve many tenants with per-tenant variable injection. And once Avoca's response to "what assistant?" is data-driven, swapping Vapi for another runtime (ElevenLabs is already wired as a fallback in some places) is a much smaller move.
Verified observations
The Twilio config for a phone number sometimes shows https://api.us.elevenlabs.io/twilio/inbound_call listed as an "accepted alternative" voice-fallback URL. That's evidence the multi-platform optionality is being actively considered, not just theoretical.
What this means for the FDE day-to-day
| Task | Legacy | Current |
|---|---|---|
| Edit the agent's prompt | Vapi UI directly | Avoca admin / blueprint editor |
| Add a new tool | Vapi UI tool definition + Avoca handler | Avoca admin (sub-agent's tool list) + Avoca handler |
| Test a prompt change locally | Configure Vapi assistant to point at your tunnel | Provision a test phone + sync (see Local Dev Setup) |
| Add an experiment / A/B test | Duplicate the Vapi assistant; route per condition | Add the variant to Avoca's dynamic-resolution logic |
| Inject runtime variables (timezone, caller-id) | Difficult; Vapi config is static | Built into the dynamic-resolution response |
If you don't know which pattern a client is on, default to assuming the current pattern is the strategic direction and the legacy pattern is being migrated away from. New work should generally aim at the current pattern's surfaces.
Related pages
- Vapi Squads — what a squad is, sub-agents, hand-offs, server-URL priority levels.
- In-Call Sequence — the runtime loop showing how the dynamic-resolution response is consumed by Vapi.
- Local Dev Setup — the FDE workflow for test-phone provisioning + tunnel routing on current-pattern clients.
- Architecture Overview — broader system context for Avoca's product surfaces beyond agents.
Open questions
These are still being mapped (see Unknowns for the running list):
- Where does the blueprint editor live in Avoca admin for current-pattern clients? (Confirmed used; UI path not yet documented.)
- How do blueprint edits propagate to existing tenants? (Edit-once-update-all? Manual rollout? Versioned with migration?)
- What's the migration path for clients still on the legacy pattern? (Per-client one-time conversion? Ongoing dual-support?)