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 via pnpm api-key). Every route under /api uses validateApiKey.
  • Agent ↔ Serverpackages/agent/src/api_client.py fetches configs via GET /api/agents/by-phone/:phoneNumber, writes call lifecycle events, and uploads transcript location & metadata.
  • Storage – Call recordings land in Azure Blob Storage (calls container) and transcripts are anonymized via Presidio before being persisted, as described in apps/docs/content/call-recording-transcription.mdx.
  • Secrets.env.example enumerates 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

EnvironmentPurposeKey Infrastructure
LocalDeveloper laptopsdocker-compose.yml Postgres, LiveKit dev stack (optional), Agent via uv, API via pnpm --filter server dev, Docs via pnpm --filter docs dev.
DevelopmentContinuous deployment targetAzure Container Apps (module.docs, module.server, module.agent), dev PostgreSQL Flexible Server, Container Registry. Auto-deployed from main after CI builds.
ProductionCustomer-facingSame 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.