Architecture
System Overview
Virsyn Voice AI follows a serverless architecture with Supabase as the backend platform. Edge Functions handle all business logic, orchestrating between Ultravox (voice AI) and Telnyx (telephony).
┌─────────────┐ ┌──────────────────────┐ ┌─────────────┐
│ Frontend │────▶│ Supabase Edge │────▶│ Ultravox │
│ (Client) │ │ Functions (Deno/TS) │ │ Voice AI │
└─────────────┘ └──────────┬───────────┘ └─────────────┘
│
┌──────────┴───────────┐
│ │
┌─────▼─────┐ ┌───────▼──────┐
│ Supabase │ │ Telnyx │
│ PostgreSQL │ │ Telephony │
└────────────┘ └──────────────┘
Components
Supabase Backend
- PostgreSQL database stores all application data: agencies, users, agent mappings, phone numbers, call records, and encrypted credentials
- Row Level Security (RLS) enforces agency-scoped data isolation
- RPC functions handle encryption/decryption of API keys (
encrypt_api_key,decrypt_api_key) - Edge Functions (Deno/TypeScript) implement all API endpoints
Ultravox Integration
Ultravox provides the AI voice engine. The integration covers:
- Agent creation with system prompts, voice selection, and tool configuration
- Call management including recordings and transcripts
- Webhook events for real-time call status updates (
call.started,call.joined,call.ended) - Voice catalog with language-specific voice options
- Tool definitions for agent capabilities during calls
Telnyx Integration
Telnyx provides telephony infrastructure:
- Phone number search and purchase across countries, area codes, and number types
- Call Control Applications that route inbound calls to Ultravox
- Outbound Voice Profiles for outbound call origination
- SIP credential import into Ultravox for call bridging
Database
The PostgreSQL database contains 17 tables organized by domain. See the Database Reference for full schema details.
| Group | Tables | Purpose |
|---|---|---|
| Core | agencies, users, clients | Multi-tenant identity and organization |
| Agents | agent_mappings, agency_tools, ultravox_voices | AI agent configuration and resources |
| Phone Numbers | agency_phone_numbers | Telnyx phone number inventory |
| Calls | calls, call_stats_daily, call_batches | Call records, analytics, and batch operations |
| Campaigns | campaigns, campaign_contacts | Outbound campaign management |
| Queues | webhook_queue, enrichment_queue | Async processing pipelines |
| Sync | historical_sync_jobs | Historical data backfill |
The schema uses 14 custom enums for type safety (e.g., user_role, call_direction, phone_number_status) and database triggers to automate the enrichment pipeline -- when a call ends, a trigger queues transcript fetching, and when the transcript completes, another trigger queues AI analysis.
Data Flow
Inbound Call Flow
- Caller dials a Telnyx phone number
- Telnyx routes the call via the Call Control Application
- Ultravox receives the call and activates the assigned agent
- Ultravox sends webhook events (
call.started,call.ended) to Virsyn - Virsyn queues and processes webhook events asynchronously
- Call data is enriched with transcripts and AI analysis
Provisioning Flow
- User saves Ultravox API key (encrypted at rest)
- User saves Telnyx credentials (API key, public key, account SID -- all encrypted)
provision-telephonycreates a Telnyx Call Control Application and Outbound Voice Profile- Telnyx SIP credentials are imported into Ultravox
- Phone numbers can then be purchased and assigned to agents
Queue-Based Processing
Several operations use asynchronous queue processing:
| Queue | Purpose | Trigger |
|---|---|---|
| Webhook queue | Process Ultravox call events | webhook-ingest enqueues, process-webhook-queue dequeues (cron) |
| Enrichment queue | Fetch transcripts, run AI analysis | Completed calls enqueued, process-enrichment-queue dequeues (cron) |
| Historical sync | Backfill past call data | start-historical-sync initiates, process-historical-sync runs in batches |
Security Model
Authentication
- User-facing endpoints require Supabase JWT Bearer tokens
- Cron/internal endpoints use service-role authentication
- Webhook ingestion uses URL-path secrets (
/webhook-ingest/{agency_id}/{secret})
Authorization
- Role-based access:
agency_owner,agency_admin,agency_member - All data queries are scoped to the user's
agency_id - Destructive operations (delete, release) require owner/admin roles
Credential Storage
- API keys for Ultravox and Telnyx are encrypted via Supabase RPC before storage
- Decryption happens at runtime only when the key is needed for an API call
- Webhook secrets are generated per-agency for event verification