Skip to content

VapiAI/homepage-voice-agents

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Vapi Voice Agents

A small, opinionated framework for bootstrapping a fleet of Vapi voice assistants from a single source of truth in code. It is the same setup that powers the multilingual voice demos on the Vapi homepage, extracted so you can read it, run it, and adapt it to your own agents.

The framework builds assistants along two axes:

  • Scenarios — what the agent is for (appointment-scheduling, qualification-screening, customer-support).
  • Languagesen, es, it, fr, and a multi variant that handles 60+ languages in one call.

Three scenarios across five languages gives you 15 assistants, all defined in code and pushed to Vapi with one idempotent command. Add a scenario or a language and the matrix grows automatically.

Why this exists

Building one assistant in the dashboard is easy. Keeping a dozen of them consistent — same guardrails, same analysis plans, the same prompt tweak applied everywhere — is the hard part. This repo keeps every prompt, voice, transcriber, and analysis plan in version control, so a single edit re-applies across the whole fleet on the next bootstrap.

Quick start

You'll need Bun and a Vapi account.

# 1. Install dependencies
bun install

# 2. Add your Vapi private key
cp .env.example .env.local
#   then edit .env.local and paste in VAPI_PRIVATE_KEY

# 3. Create the fleet (first run creates all 15 assistants)
bun run bootstrap

The first run prints the new assistant ids:

✓ Created appointment-scheduling/en → 1a2b3c...
...
Add these to .env.local:
  VAPI_ASSISTANT_APPOINTMENT_SCHEDULING_EN_ID=1a2b3c...
  ...

Paste those into .env.local, then run bun run bootstrap again. The second run finds the ids and updates each assistant in place instead of creating duplicates. From then on, editing a prompt and re-running is all it takes to ship a change.

How it's organized

src/
  types.ts            # Scenario + language ids, env-var naming, labels
  languages.ts        # Voice + transcriber stack, keyed by language
  loadPrompt.ts       # Composes a system prompt from markdown fragments
  buildAssistant.ts   # Stitches everything into one Vapi assistant body
  bootstrap.ts        # Idempotent create/update loop (the entry point)

  scenarios/
    index.ts                    # Scenario registry
    appointment-scheduling.ts   # One scenario = plain data + tools + analysis
    qualification-screening.ts
    customer-support.ts

  prompts/
    shared/
      language-en.md            # `## Language` section for EN
      language-multi.md         # `## Language` section for Multi
      preambles/{es,fr,it}.md   # Per-language preamble prepended to ES/FR/IT
    appointment-scheduling/
      body.md                   # Shared body for all 5 language variants
      off-topic-{es,fr,it}.md   # Localized off-topic redirect lines
    qualification-screening/
      ...
    customer-support/
      body.md
      idle-hook.md              # Prompt for the silence-timeout hook
      off-topic-{es,fr,it}.md

assistants/                     # Generated raw config snapshot (see below)
  appointment-scheduling/{en,es,it,fr,multi}.json
  qualification-screening/{en,es,it,fr,multi}.json
  customer-support/{en,es,it,fr,multi}.json

One body, five languages

Each scenario has a single body.md. It contains exactly one {{LANGUAGE_SECTION}} placeholder, and loadPrompt.ts resolves it per language:

  • EN → placeholder replaced with shared/language-en.md.
  • Multi → placeholder replaced with shared/language-multi.md.
  • ES / FR / IT → placeholder stripped, and shared/preambles/<lang>.md is prepended (with that scenario's localized off-topic lines spliced in).

So a wording change shared by every variant of a scenario is one edit to body.md, applied to all five assistants on the next bootstrap.

The voice & transcriber stack splits at language

languages.ts holds two records keyed by language: one for the voice, one for the transcriber. English uses a Deepgram + ElevenLabs eleven_turbo_v2 stack; every non-English variant (including multi) runs Soniox with a Deepgram fallback and an ElevenLabs multilingual voice. Adding a sixth language is one new entry in each record.

Per-scenario voice tweaks (a specific voice id, speed, stability) live on the scenario as voiceOverrides and are spread on top of the language defaults in buildAssistant.ts.

Adapting it to your own agents

  • Change what an agent says → edit the relevant prompts/<scenario>/body.md (or a shared/ fragment), then re-run bun run bootstrap.
  • Add a scenario → add prompts/<scenario>/body.md, a scenarios/<scenario>.ts file, then register it in scenarios/index.ts and types.ts.
  • Add a language → add the id to types.ts, a voice + transcriber entry in languages.ts, and a ## Language section or preamble under prompts/shared/.
  • Customer-support knowledge baseprompts/customer-support/body.md ships with two placeholder knowledge items under "Knowledge base — REFERENCE ONLY." Replace them with your own product facts so the agent has something real to answer from.

Raw assistant configs

The assistants/ folder holds a JSON snapshot of every assistant — one file per (scenario × language) tuple. Each file is the exact body the bootstrap script sends to assistants.create / assistants.update, so it's a readable record of the fully composed prompt, voice, transcriber, tools, and analysis plan that goes live on Vapi.

Regenerate the snapshot any time the prompts or config change:

bun run dump

These files are generated artifacts — src/ stays the single source of truth. They're committed so you can diff config changes in review and import a config into another tool without running anything.

Notes

  • The bootstrap is the single source of truth. One-off edits in the Vapi dashboard work, but the next bootstrap run overwrites them — keep anything you want to persist in code.
  • The capture tools (capture_lead, capture_appointment, capture_intent) are client-side (async: true, no server URL). They fire structured data to the browser mid-call without a backend round-trip. If you drive calls server-side instead, point them at a server.url and handle the result there.

About

The exact voice agents from vapi.ai, as code — bootstrap the whole fleet into your own Vapi dashboard with one command.

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages