Manage your Vapi voice agents as code. Assistants, squads, tools, structured outputs, simulations and evals live as YAML and Markdown files in git, and this repository's CLI syncs them to your Vapi orgs, promotes them from development to production, and tests every pull request with simulations before it merges.
New to Vapi? Start with the Vapi documentation, then come back here to manage what you build.
- Safe sync.
npm run applypulls the latest platform state before it pushes, so edits made in the dashboard aren't silently overwritten. Every deploy writes a snapshot you can roll back to. - Any number of orgs. Each Vapi org is a folder. Promote resources between them (dev → staging → production) with credentials and phone numbers bound per org.
- Tests on every pull request. Simulation suites run against the branch's
own files, with tools mocked and nothing deployed, and report a
Vapi Evalsstatus that links to the run. - A field guide to Vapi.
docs/learnings/collects hard-won gotchas and recipes for assistants, squads, transfers, voicemail detection, latency and more. - Ready for coding agents.
AGENTS.mdandCLAUDE.mdteach Claude Code, Cursor and Codex how to work in the repo safely.
flowchart LR
pr["Pull request<br/>validation and simulation checks,<br/>nothing deployed"] -- merge --> dev
subgraph git["Your git repository"]
direction TB
dev["resources/dev/<br/>assistants, tools, squads…"]
prod["resources/prod/"]
dev -- "npm run promote" --> prod
end
subgraph vapi["Vapi"]
direction TB
devOrg["dev org"]
prodOrg["prod org"]
end
dev -- "npm run apply" --> devOrg
devOrg -. "npm run pull" .-> dev
prod -- "applied by promote" --> prodOrg
You edit files and review changes in pull requests. The CLI turns readable
references (toolIds: [lookup-patient]) into the UUIDs each org uses, so the
same files work in every org.
| Dashboard / Ad-hoc API | GitOps | |
|---|---|---|
| History | Limited visibility of who changed what | Full git history with blame |
| Review | Changes go live immediately (can break things) | PR review before deploy |
| Rollback | Manual recreation | git revert, then npm run apply |
| Environments | Tedious to copy-paste between envs | Same config, different state files |
| Collaboration | One person at a time. Need to duplicate assistants, tools, etc. | Team can collaborate and use git branching |
| Reproducibility | "It worked on my assistant!" | Declarative, version-controlled |
| Disaster Recovery | Hope you have backups | Re-apply from git |
Your copy will hold your agents' prompts and configuration, so make it a private repository. Pick one:
-
Keep upstream history (recommended). You can pull in future updates with an ordinary merge.
git clone https://github.com/VapiAI/gitops.git my-vapi-gitops cd my-vapi-gitops git remote rename origin upstream git remote add origin <your-private-repo-url> git push -u origin main
-
Use this template. Click Use this template on GitHub for a fresh repository with clean history. Updates are still possible, but the first one needs an extra step (see Staying up to date).
Avoid a public fork: it would publish your configuration.
You need Node.js 20.12+ or 22.13+ (.nvmrc pins 22) and a Vapi private API
key for each org, from dashboard.vapi.ai/org/api-keys.
nvm use
npm cinpm run setupThe wizard asks for your API key (and detects the US or EU region), a name for
the org's folder (for example my-org), and which existing resources to
download, offering their dependencies too. It creates .env.my-org (your key, gitignored) and
resources/my-org/. Run it again to add more orgs.
The wizard needs a real terminal. Coding agents (Claude Code, Cursor, Codex, …) and CI run commands without one, so pass the org name to skip every prompt:
# Option A — a human creates the env file, so the key never passes through the agent
cp .env.example .env.my-org # then paste the private API key into VAPI_PRIVATE_API_KEY
npm run setup -- my-org
# Option B — the key is already in the environment (CI secret, shell export)
VAPI_PRIVATE_API_KEY=... npm run setup -- my-org| Option | Default | Meaning |
|---|---|---|
--region us|eu |
VAPI_BASE_URL if set, else auto-detect (US, then EU) |
Which Vapi API to use |
--resources all|none |
all |
all downloads every resource into resources/<org>/; none only seeds .vapi-state.<org>.json (use when you'll author from scratch) |
The private API key is never accepted as a command-line flag (it would leak into shell history and
agent transcripts). Non-interactive setup also refuses to run if resources/<org>/ or
.vapi-state.<org>.json already exists — use npm run pull -- <org> to refresh an existing org.
Edit a file under resources/my-org/ (or start from the
starter example), then:
npm run validate -- my-org # schema check, no network
npm run apply -- my-org # pull the latest, merge, pushCommit the changed files and .vapi-state.my-org.json so your team shares the
same name → UUID mappings. Every pull request runs the same validator for
every org in CI (the Validate resources check), so a config apply would
refuse fails before it merges.
npm run call -- my-org -a my-assistant # talk to it from your terminal
npm run sim -- my-org --suite core --target my-squad # run a simulation suiteTo test every pull request automatically, set up PR checks.
- One folder per org.
resources/<org>/holds that org's resources, in folders by type (assistants/,tools/,squads/,structuredOutputs/,evals/,simulations/). Org names are yours to choose (acme-dev,acme-prod). - Resources refer to each other by ID. A resource's ID is its path under
the type folder, without the extension:
tools/lookup-patient.ymlislookup-patient. Never paste UUIDs into resource files. - The state file maps IDs to UUIDs.
.vapi-state.<org>.jsonrecords which platform resource each file is, per org. It is committed and holds no secrets. - Secrets stay out of git. API keys live in
.env.<org>(gitignored) or CI secrets. Credentials are referenced by name and bound to each org's own credentials. applyis the default deploy. It pulls first, so it won't overwrite changes made in the dashboard since your last pull.pullonly syncs down; rawpushskips the pull and is rarely what you want.
| Path | Committed? | What it is |
|---|---|---|
resources/<org>/ |
Yes | Your resources |
.vapi-state.<org>.json |
Yes | Name → UUID mappings for the org |
.env.<org> |
No | API key and generated binding IDs |
.vapi-state-hash/ |
No | Your local baseline for detecting dashboard edits |
.vapi-state.<org>.snapshots/ |
No | Pre-deploy snapshots for npm run rollback |
More detail: How the engine works.
| Command | What it does |
|---|---|
npm run setup |
Connect an org: create .env.<org> and resources/<org>/ |
npm run validate -- <org> |
Check resource files offline before deploying |
npm run apply -- <org> |
Deploy: pull, merge, then push (the default) |
npm run pull -- <org> |
Sync platform changes down without overwriting local edits |
npm run push -- <org> |
Push without pulling first (prefer apply) |
npm run rollback -- <org> |
Restore a pre-deploy snapshot |
npm run cleanup -- <org> |
Find, and optionally delete, platform resources with no file |
npm run audit -- <org> |
Report drift between files, state and the platform |
npm run call -- <org> |
Talk to an assistant or squad from your terminal |
npm run sim -- <org> |
Run a simulation suite against deployed resources |
npm run check |
Run PR simulation checks against local files |
npm run promote |
Promote resources from one org to the next |
setup, apply, pull, push, cleanup and call prompt interactively
when run without arguments. Full flags and examples:
Commands.
| Guide | Read it to… |
|---|---|
| Everyday workflows | Deploy, pull safely, recover from a bad deploy, clean up |
| File formats | Write assistants, tools, squads, structured outputs and simulations |
| Resource reference | Look up every setting, with examples |
| Writing system prompts | Structure a voice agent's prompt |
| PR checks | Test every pull request with simulations |
| Promotion | Move resources from dev to staging to production |
| How the engine works | Understand sync, references, credentials and state |
| Configuration | Environment variables and secrets |
| Troubleshooting | Fix common errors |
docs/learnings/ is a library of behaviours the
API reference doesn't spell out, collected from real deployments. Some
starting points:
| If you're working on… | Read |
|---|---|
| Transfers that don't connect | transfers.md |
| Squads and handoffs | squads.md |
| Voicemail vs human detection | voicemail-detection.md |
| Making your agent faster | latency.md |
| Simulations and test suites | simulations.md |
| Prompt writing | Vapi Prompt Optimization Guide |
| Resource | Format |
|---|---|
| Assistants | .md (system prompt as Markdown) or .yml |
| Tools | .yml |
| Structured Outputs | .yml |
| Squads | .yml |
| Personalities | .yml |
| Scenarios | .yml |
| Simulations | .yml |
| Simulation Suites | .yml |
| Evals | .yml |
Any resource can also be a .ts file that default-exports the resource
object. API references: Assistants,
Tools,
Structured Outputs,
Squads,
Evals.
The engine (src/, tests/, .github/workflows/, package*.json) comes from
upstream. Your configuration (resources/, .vapi-state.*.json,
promotion.yml, vapi-checks.yml) is yours; upstream only ships *.example
files there. Avoid editing engine files, so updates merge cleanly.
git fetch upstream
git merge upstream/main
npm ci && npm run build && npm testIf you started from Use this template, add the remote first with
git remote add upstream https://github.com/VapiAI/gitops.git; the first merge
also needs --allow-unrelated-histories, and you'll resolve conflicts once.
Never commit API keys: .env.* files are gitignored, and the CLI never accepts
a key as a command-line flag. .ts resource files execute when loaded, so
review them like code. To report a vulnerability, see SECURITY.md.
Issues and pull requests are welcome; see CONTRIBUTING.md. Licensed under Apache 2.0.