Introduction
Why we built the platform this way, which services it depends on, and how each environment is structured.
Introduction to echo Agent
echo Agent is a multi-tenant voice AI platform that pairs a Python-based LiveKit agent with a TypeScript API surface. The monorepo (root echo/) uses Turborepo so the agent (packages/agent), server (packages/server), docs (apps/docs), and infrastructure code (packages/terraform) stay in sync.
Core Technologies and Why We Use Them
- LiveKit Agents + Python (
packages/agent) – Handles low-latency SIP audio, multilingual speech, and transfers. LiveKit bundles the STT, TTS, and LLM models (OpenAI, Cartesia, AssemblyAI) and gives us SIP + WebRTC connectivity, so we avoid building our own media pipeline. - Express.js API (
packages/server) – REST surface for provisioning, companies, agents, and call records. We chose Express with TypeScript for familiarity, middleware ecosystem, and native support for streaming handlers used by the agent. - Drizzle ORM + PostgreSQL – All relational data (companies, agents, calls) lives in Postgres. Drizzle gives us type-safe SQL, generated migrations, and easy UUID support. Local instances run through
docker-compose.yml, while Azure hosts Flexible Server in the cloud. - Twilio + LiveKit SIP Fabric – Telephony provisioning (
routes/telephony.route.ts) wires Twilio numbers into LiveKit SIP trunks so inbound calls can wake the agent. Twilio handles PSTN reach, LiveKit handles agent media. - Azure Container Apps + Terraform (
packages/terraform) – Each deployable (docs, server, agent) becomes its own container app. Terraform keeps dev/prod environments identical, while GitHub Actions build Docker images and push tagged releases to Azure Container Registry. - Fumadocs + Next.js (
apps/docs) – This site keeps runbooks, API how-to’s, and ops notes close to the code.
Access Patterns
- Server REST API – Protected with bearer tokens from
API_KEYS(generate viapnpm api-key). Every route under/apiusesvalidateApiKey. - Agent ↔ Server –
packages/agent/src/api_client.pyfetches configs viaGET /api/agents/by-phone/:phoneNumber, writes call lifecycle events, and uploads transcript location & metadata. - Storage – Call recordings land in Azure Blob Storage (
callscontainer) and transcripts are anonymized via Presidio before being persisted, as described inapps/docs/content/call-recording-transcription.mdx. - Secrets –
.env.exampleenumerates LiveKit, Azure OpenAI, and registry settings. Container Apps pull matching secrets at runtime; Terraform output names map 1:1 to GitHub Action secrets (GITHUB_SECRETS.md).
Environments
| Environment | Purpose | Key Infrastructure |
|---|---|---|
| Local | Developer laptops | docker-compose.yml Postgres, LiveKit dev stack (optional), Agent via uv, API via pnpm --filter server dev, Docs via pnpm --filter docs dev. |
| Development | Continuous deployment target | Azure Container Apps (module.docs, module.server, module.agent), dev PostgreSQL Flexible Server, Container Registry. Auto-deployed from main after CI builds. |
| Production | Customer-facing | Same modules in packages/terraform/production. Deployments are manual GitHub Action approvals with pre-deploy database migrations. |
Each environment runs the same Docker images (built from each package’s Dockerfile). Before Container Apps roll out a new revision, the GitHub workflow triggers pnpm db:migrate inside a migration job (see MIGRATION_SETUP.md) to keep Postgres schemas aligned.
Use the rest of this documentation set chronologically: start with Getting Started, walk through the How-To guides, review Agent configuration, then finish with Deployment procedures.