Conversation recording
When enabled for a project, VoiceThere stores voice-session audio (user microphone + agent TTS playback) for dashboard playback on the session detail page. Recording is opt in— default off — and scoped to the authenticated project's settings and subscription. Cross-project access is denied.
Where to configure
- Dashboard: Project overview → Session settings → Conversation recording (
conversation_recording_enabled) and Metered recording overage (conversation_recording_metered_overage_enabled). - CLI:
voicethere projects session-settings list/voicethere projects session-settings set conversation_recording_enabled <true|false> - API:
PATCH /projects/:projectId/session-settings— see Control plane API.
Defaults: conversation_recording_enabled is false; conversation_recording_metered_overage_enabled is false. Settings apply on the next Deploy to cloud.
Included minutes & overage
Each subscription tier includes a monthly pool of recorded minutes (UTC calendar month). Billable duration uses fractional minutes (seconds ÷ 60), not rounded-up whole minutes per session. Retention follows your plan.
| Plan | Included min / month | Retention |
|---|---|---|
| Free | 60 | 7 days |
| Budget | 120 | 14 days |
| Budget+ | 240 | 14 days |
| Advanced | 500 | 31 days |
| Ultimate | 1,000 | 31 days |
Rollover vs metered overage
With metered recording overage off (default), unused included minutes at the UTC month boundary roll into a capped minute bank(bank size capped at one month's included allowance). Recording stops when included + bank are exhausted.
With metered recording overage on, unused minutes do not roll over. Duration past included + bank bills at 1 credit per minuteon paid tiers (fractional — e.g. ~72 s ≈ 1.2 credits, not a rounded-up whole minute). Free cannot use metered recording overage. Metered overage requires org billing readiness — a payment method on file and effective metered toggles — same gate as session billable minutes. See Billing & usage credits.
Dashboard playback
Open Project → Sessions → session detail. When recording was enabled for the project at session time, an audio player loads a signed URL for the stored session mix. If recording is disabled or no audio was captured, the panel explains how to enable recording on the project.
Project members with write access can delete a session recording from the session detail page (Delete recording) or via the API. Deletion removes the stored audio object and the database row; it does not refund included or metered recording minutes.
Stereo channel layout
Session recordings are stereo at 48 kHz. The left channel is the agent voice (TTS playback). The right channel is the caller (user microphone). This layout is useful when reviewing on headphones or in a DAW; mono speakers sum both channels together.
Delete a recording
- Dashboard: Project → Sessions → session detail → Delete recording (confirm dialog).
- API:
DELETE /projects/:projectId/sessions/:orchestratorSessionId/recording— returns204when the recording row and storage object are removed. Project scope is resolved from your API key or dashboard session (not a client-supplied project id alone). - CLI:
voicethere sessions recording delete <sessionId>(planned — use the API or dashboard until the CLI command ships).
Agent recording controls
When the project setting is on and the session connects, the runner begins capturing audio automatically. Use @voicethere/agent helpers with the session id to pause, resume, or stop capture — the runner owns the file. Typical reasons to call them:
- Consent — if the caller declines recording, call
stopRecordingimmediately (capture already started when the session connected).startRecording/resumeRecordingrestarts after a pause or stop. - Sensitive input — when the caller is about to share a card number, SSN, password, or other PII,
pauseRecordingso those frames are omitted from the file;resumeRecordingafterward. - End of capture —
stopRecordingwhen the recorded part of the call is finished (session end or mid-call).
Project setting conversation_recording_enabled must be true (and deployed) or the runner will not capture or ingest audio. Check ctx.recordingAvailable on onSessionStart before prompting for consent. Plans still need remaining included, bank, or metered allowance.
Recording helpers return a promise that resolves when the runner acknowledges the control message. Local verify runs without a runner parent resolve immediately with reason: "local_mock" so laptop testing never blocks on IPC.
Skip recording when the customer declines
import {
defineAgent,
speak,
stopRecording,
} from "@voicethere/agent";
export default defineAgent({
async onSessionStart({ sessionId, recordingAvailable }) {
if (!recordingAvailable) return;
await speak(
sessionId,
"This call may be recorded for quality. Say no if you do not want recording.",
);
},
async onUserSpeechFinal({ sessionId, text }) {
const answer = text.trim().toLowerCase();
if (/\bno\b|do not|don't|decline/.test(answer)) {
const result = await stopRecording(sessionId);
if (!result.ok) return;
await speak(sessionId, "Understood — we will not record this call.");
}
},
});Pause while collecting sensitive information
import {
defineAgent,
pauseRecording,
resumeRecording,
speak,
stopRecording,
} from "@voicethere/agent";
export default defineAgent({
async onSessionStart({ sessionId, recordingAvailable }) {
if (!recordingAvailable) return;
// Runner already recording when the project setting is on.
},
async onUserSpeechFinal({ sessionId, text }) {
// About to collect payment details — omit audio until done
if (/credit card|card number|cvv|social security|password/i.test(text)) {
const pause = await pauseRecording(sessionId);
if (!pause.ok) return;
await speak(
sessionId,
"I will pause recording while you share that. Say continue when you are done.",
);
return;
}
if (/\bcontinue\b|done|finished/i.test(text)) {
await resumeRecording(sessionId);
}
},
async onSessionEnd({ sessionId }) {
await stopRecording(sessionId);
},
});Multi-tenant isolation
Recordings, minute banks, and playback URLs are keyed to the projectthat owns the session. API routes resolve the project from your API key or dashboard session — never from a client-supplied project id alone. One project cannot stream or bill against another project's recording allowance.
CLI examples
# Enable recording (default off) voicethere projects session-settings set conversation_recording_enabled true voicethere deploy --wait # Rollover mode (default) — bank unused minutes at month end voicethere projects session-settings set conversation_recording_metered_overage_enabled false # Metered overage — 1 credit/min past included + bank (paid tiers + billing ready) voicethere projects session-settings set conversation_recording_metered_overage_enabled true # Download a session recording (waits until ready, writes Opus file) voicethere sessions recording <orchestratorSessionId> --wait --output ./session.opus # Metadata only (status, duration_ms, …) voicethere sessions recording <orchestratorSessionId> --json
Related
- Conversation history — STT/TTS text transcripts (separate from audio recording)
- Billing & usage credits
- Runner settings