VoiceThere cloud CLI for API login, project management, and agent bundle upload.
Requires Node.js 22+.
Release flow: build upload stores a bundle in history; build promote <buildId> sets the active build in the control plane only; deploy --wait promotes (if needed) and rolls out to cloud runners.
npm install -g @voicethere/cliOr run without a global install:
npx @voicethere/cli <command>Commands print resolved paths on stderr at startup:
[voicethere] project: 550e8400-… (project config (/path/.voicethere/config.json))
[voicethere] bundle: /path/to/dist/agent.js (project config (/path/.voicethere/config.json))
Use this when a command fails to confirm which project and bundle file were picked up.
The CLI remembers the active project in .voicethere/config.json (safe to commit — no API keys).
| Situation | What happens |
|---|---|
| Config file present | build upload, build list, build promote use its project_id automatically |
| No config, interactive terminal | projects use shows a numbered project picker |
| No config, CI / script | Run voicethere projects use <projectId> once (or commit config from projects create) |
You do not need to run projects use on every command when the config file is already in the repo.
Credentials live in ~/.config/voicethere/credentials.json (mode 0600) — not in your agent repo. Never put API keys in .voicethere/config.json.
Default — browser login (recommended for interactive machines):
voicethere login
# Opens the dashboard approval page, then stores a personal API key (vthu_…)
# and active organization. If a project is linked in .voicethere/config.json,
# login also checks that your account can access that project.| Flag | Behavior |
|---|---|
| (none) | Validate existing credentials; skip if they still work (see below) |
--force |
Start a new browser authorization even when local credentials are valid |
--no-open |
Print the verification URL and user code; do not open a browser |
--api-base <url> |
Override API base for this login (also honored from env / saved file) |
If credentials already work, voicethere login skips creating another authorization:
- With a linked project (
.voicethere/config.json): requires a successful project fetch. - Without a linked project: requires a successful project list.
- Auth failures (
401/403/404/ TOS not accepted) start browser login again. - Network / server errors abort without rewriting credentials — fix connectivity, or use
--force.
Manual API key login (CI, automation, or when you already have a key):
voicethere login --api-key vth_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# personal key for org/account commands (dashboard Settings → API keys):
voicethere login --api-key vth_… --user-api-key vthu_…
# staging / custom API host:
voicethere login --api-key "$VOICETHERE_API_KEY" --api-base https://app.voicethere.dev/api/v1Default API base: https://app.voicethere.dev/api/v1
Credentials & environment precedence (env wins over the credentials file):
| Variable | Overrides |
|---|---|
VOICETHERE_API_KEY |
Org / project API key (vth_ / vthc_) |
VOICETHERE_USER_API_KEY |
Personal API key (vthu_) |
VOICETHERE_API_BASE |
API base URL |
VOICETHERE_ORG_ID |
Active organization for personal keys |
VOICETHERE_CREDENTIALS_PATH |
Credentials file location (tests / isolated runs) |
VOICETHERE_PROJECT_CONFIG |
Path to .voicethere/config.json |
After browser login, if those env vars are set, the CLI prints a warning that they override the newly saved file. Personal-key requests send x-voicethere-org-id when an active org is selected (orgs use).
Security notes
- Credentials file is written atomically with mode
0600. - Browser login mints a personal key (
vthu_), clears any stored org/projectapi_keyfrom the credentials file (so interactive commands use the new personal key), and does not put secrets in project config. ExplicitVOICETHERE_API_KEY/VOICETHERE_USER_API_KEYremain authoritative when set — browser login aborts if those env vars are present so a saved key cannot silently lose to a broken env override. - After approval, the CLI verifies a linked project with the minted personal key + returned active org before reporting success.
--forcereplaces the local personal key for this machine; it does not revoke keys on other devices.- Linked-project mismatch: the CLI keeps
.voicethere/config.jsonunchanged and fails login (nonzero) if the new personal key cannot access that project. - Prefer personal keys over the legacy
--dashboard-cookiepath.
More detail: CLI login guide on the VoiceThere docs site.
From your agent project root (where you build dist/agent.js):
npm install @voicethere/agent
npx @voicethere/agent build
# Creates the cloud project and uses it (.voicethere/config.json)
voicethere projects create "My Voice Agent"
git add .voicethere/config.json
git commit -m "chore: use VoiceThere project"Upload stores a new immutable build in history — it does not go live yet.
voicethere build validate # optional; uses config bundle or dist/agent.js
voicethere build upload -m "Add Spanish greeting and fix barge-in"-m / --message is like a git commit message: a short label so you can tell builds apart in build list and the dashboard.
Promote sets the active build in the VoiceThere control plane only. To roll out to cloud runners, run voicethere deploy --wait (promote + cluster rollout in one step).
Pass the build UUID from build upload or build list, or omit it in an interactive terminal to pick from a list:
voicethere build list
voicethere build promote <build-uuid>
# interactive: voicethere build promoteTypical release loop:
npx @voicethere/agent build
voicethere build upload -m "v0.2 — shorter silence timeout"
voicethere build promote <build-uuid-from-upload-or-list>git clone <your-agent-repo>
cd <your-agent-repo>
voicethere login --api-key "$VOICETHERE_API_KEY"
npx @voicethere/agent build
voicethere build upload
voicethere build list
voicethere build promote <build-uuid>No projects use needed — the active project travels with the repo.
voicethere projects list
voicethere projects use <uuid> --slug my-agent --bundle dist/agent.js
git add .voicethere/config.json && git commit -m "chore: use VoiceThere project"Re-run voicethere projects use with no args to confirm the current project (reads existing config).
Inspect the selection anytime:
voicethere projects showWith committed .voicethere/config.json (typical agent repo):
voicethere login --api-key "$VOICETHERE_API_KEY"
npx @voicethere/agent build
voicethere build upload -m "$GITHUB_SHA — $GITHUB_REF_NAME" --skip-validate
voicethere build promote <build-uuid-from-upload>No projects use step — the config file is the active project.
Without a config file (one-off job cwd):
voicethere login --api-key "$VOICETHERE_API_KEY"
voicethere projects use <project-uuid>
npx @voicethere/agent build
voicethere build upload --skip-validate
voicethere build promote <build-uuid-from-upload>Other CI notes:
- Credentials:
VOICETHERE_CREDENTIALS_PATHfor isolated test runs - Config override:
VOICETHERE_PROJECT_CONFIG=/path/to/config.json
Split upload and promote across jobs if you want a human approval gate between them.
voicethere deploy --wait promotes the build (when needed) and rolls out to cloud runners, blocking until the deployment is active (or failed).
voicethere build upload -m "v0.2 — shorter silence timeout"
voicethere deploy --wait
# or pin a build: voicethere deploy --wait --build-id <build-uuid>Use build promote alone when you only need to update the control plane (e.g. smoke tests); use deploy --wait for anything that must run on staging runners.
Per-agent-repo file: .voicethere/config.json
| Field | Purpose |
|---|---|
project_id |
Active platform project UUID |
project_slug, name |
Human-readable metadata (optional) |
bundle |
Default bundle path (default dist/agent.js) |
Secrets stay global: ~/.config/voicethere/credentials.json (from voicethere login).
Example: .voicethere/config.json.example
| Command | Description |
|---|---|
login [--force] [--no-open] [--api-base <url>] |
Browser device login (default); skip if credentials still work |
login --api-key <key> [--user-api-key <vthu>] [--api-base <url>] [--dashboard-cookie <cookie>] |
Manual key login (CI / automation); optional personal key |
orgs list |
List organizations (* = active); requires user API key or legacy cookie |
orgs use <orgId> |
Set active organization (persists active_org_id for user keys) |
org transfer-ownership <userId> |
Transfer org ownership (owner only) |
account deletion preview |
JSON preview of owned orgs + deletion job |
account deletion request-code |
Email 6-digit deletion verification code |
account deletion confirm <code> [--wait] |
Queue account deletion; --wait blocks until teardown completes |
projects list |
List org projects |
projects create <name> [--slug <slug>] |
Create project; uses it (writes config) |
projects use [projectId] |
Use project (picker or existing config when omitted) |
projects show |
Print .voicethere/config.json |
projects delete [projectId] [--force] [--wait] |
Delete project + builds (type name to confirm, or --force; --wait polls async deletion) |
projects settings list |
set Runner pool settings (warm pool, scale-down) |
projects session-settings list |
set WebRTC idle timeout + crash error message (see below) |
projects errors list [--session] [--json] |
Structured session errors from dashboard API |
projects voice catalog |
show STT/TTS vendors and models |
build list |
Builds for the active project |
build validate [file] |
Sandbox verify (default bundle from config) |
build upload [file] [-m <msg>] |
Upload to active project |
build promote [buildId] |
Promote on active project (picker when omitted in TTY) |
deploy [--wait] [--build-id] |
Promote (if needed) + cloud rollout; --wait blocks |
Progress and resolved paths go to stderr as [voicethere] … so stdout stays clean for JSON/tables (scripts can pipe stdout).
| Mode | Flag / env | What you get |
|---|---|---|
| Default | (always on) | High-level steps: “Creating project…”, “Uploading bundle…”, resolved project/bundle paths |
| Verbose | -v / --verbose or VOICETHERE_VERBOSE=1 |
API method + path, request bodies (no secrets), response status + timing, counts |
Examples:
voicethere build upload
# stderr: [voicethere] Uploading agent bundle
# [voicethere] project: …
# [voicethere] bundle: …
# [voicethere] Validating bundle locally before upload
# [voicethere] Running @voicethere/agent verify on bundle
# [voicethere] Uploading bundle to control plane API
voicethere -v build upload
# …plus [voicethere:verbose] api: https://…
# [voicethere:verbose] POST /projects/…/builds
# [voicethere:verbose] response: 201 (842ms)Global -v works on any subcommand: voicethere -v projects list.
Per-project WebRTC idle timeout and crash TTS message. Changes apply on the next voicethere deploy --wait (runner env).
voicethere projects session-settings --help # all keys, defaults, ranges
voicethere projects session-settings list
voicethere projects session-settings set idle_timeout_seconds 90
voicethere projects session-settings set data_only_idle_timeout_seconds 60
voicethere projects session-settings set error_message "Sorry, something went wrong."| Key | Type | Default | Notes |
|---|---|---|---|
idle_timeout_enabled |
bool | true |
false keeps sessions billable longer |
idle_timeout_seconds |
30–120 (org may allow higher via API) | 30 |
Voice / both projects |
data_only_idle_timeout_seconds |
30–120 (org may allow higher via API) | 30 |
Data-only: no client→server DC traffic |
idle_timeout_voice_activity |
bool | true |
Reset on speech / agent TTS (voice / both) |
idle_timeout_dc_inbound |
bool | true |
Reset on client data-channel sends |
error_message |
string | (none) | Crash TTS in voice mode |
Boolean values for set: true / false / 1 / 0 / yes / no.
List recent voice sessions (orchestrator session id, status, billable seconds):
voicethere sessions list <projectId> --start 0 --end 50
# or with .voicethere/config.json:
voicethere sessions list --start 0 --end 50The API returns { sessions, start, end, count } (max 50 rows per page). The CLI prints a Showing X–Y of Z sessions footer.
After a call ends (runner keep-alive billing), fetch billable duration:
voicethere sessions billing <orchestratorSessionId> --project <projectId>
voicethere sessions billing <orchestratorSessionId> --jsonThese commands call the platform control-plane API (GET /api/v1/projects/:id/sessions).
npm ci
npm run test:ci
node dist/cli.js --helpContributing: every new command, setting key, or API field needs Vitest coverage in the same PR (src/**/*.test.ts). See workspace rule feature-tests-required.mdc in the development repo.
Credentials path override for tests:
export VOICETHERE_CREDENTIALS_PATH=/tmp/voicethere-credentials.jsonMIT — see LICENSE.