Recordings
A recording is the stored audio of a call or conference. voepy captures
it for you, stores it, and hands back a downloadable file plus metadata
(duration, format, channel layout, size). You list, fetch, download,
and delete recordings through the /v1/recordings resource.
All paths under
/v1/.Authorization: Bearer <api-key>required. Bodies are JSON, field namessnake_case, timestamps ISO-8601, IDs opaque prefixed strings. A recording id looks likevoepy_rec_….
How recordings get created
You never POST to /v1/recordings. A recording row appears as a
side effect of a record action on a live call or conference. The
flow is:
- You start recording on an in-progress call or conference (or ask for recording at dial/answer time).
- The audio is captured for as long as recording is active.
- When recording stops — on hangup, on an explicit stop, or at a configured limit — the audio is finalized and stored.
- voepy fires a
recording.completedwebhook and the recording becomes retrievable atGET /v1/recordings/{id}.
Recording a call
Two ways to record a call:
- At dial or answer time. Pass
record(and optionallyrecord_channels,record_format) when you place an outbound call or answer an inbound one. See Calls. - Mid-call. Toggle recording on an answered call with the record actions:
POST /v1/calls/{id}/actions/start_recording
{ "channels": "dual", "format": "mp3", "play_beep": true }
POST /v1/calls/{id}/actions/stop_recording
{}
Full action reference (including max_length) lives in
Calls.
Recording a conference
Conferences record at the bridge level — one file for the whole room — and support pause/resume on top of start/stop:
POST /v1/conferences/{id}/actions/record_start
POST /v1/conferences/{id}/actions/record_pause
POST /v1/conferences/{id}/actions/record_resume
POST /v1/conferences/{id}/actions/record_stop
A conference recording is stored against the conference's host call, so
it shows up in that call's recordings array and is filterable by that
call_id. See Conferences for the action bodies.
The Recording resource
{
"id": "voepy_rec_01HXYZ123abc456def789012abcdef10",
"call_id": "voepy_call_01HXYZ123abc456def789012abcdef01",
"channels": "dual",
"format": "mp3",
"duration_seconds": 132,
"size_bytes": 671088,
"download_url": "https://media.voepy.com/recordings/voepy_rec_…mp3?sig=…",
"status": "available",
"created_at": "2026-05-12T14:33:01.880Z"
}
| Field | Type | Notes |
|---|---|---|
id | voepy_rec_… | Opaque recording id. |
call_id | voepy_call_… | The call this recording belongs to. For a conference recording, this is the host call. |
channels | enum | single (mono, both parties mixed) or dual (stereo, each leg on its own channel). |
format | enum | mp3 or wav. |
duration_seconds | integer | Recorded length in whole seconds. |
size_bytes | integer | File size of the stored audio. |
download_url | string (URL) | Time-limited URL to the audio file. May be empty until the recording is finalized. See Downloading. |
status | enum | available, archived, expired, or failed. See below. |
created_at | timestamp | When the recording row was stored (recording finalized). |
Status values
| Status | Meaning |
|---|---|
available | Stored and downloadable. |
archived | Soft-deleted by you (via DELETE). The row remains for your history; the audio is no longer served. |
expired | Past its retention window; audio no longer available. |
failed | Capture or storage errored — no usable audio. You'll have gotten a recording.failed webhook. |
A recording is downloadable only while status == available. Fetching
audio for any other status returns 409 recording_not_ready.
List recordings
GET /v1/recordings
?call_id=voepy_call_01HXYZ123abc456def789012abcdef01
&page=1
&page_size=50
| Query param | Default | Notes |
|---|---|---|
call_id | — | Filter to one call's recordings (includes any conference recording hosted on that call). |
page | 1 | 1-indexed page number. |
page_size | 20 | Rows per page, max 200. |
Results are scoped to your tenant and ordered newest-first. The response is the standard page envelope:
{
"count": 3,
"next": "https://api.voepy.com/v1/recordings/?page=2",
"previous": null,
"results": [ /* Recording objects */ ]
}
next/previous are null at the ends. Recordings also appear inline
on the parent Call resource (recordings: [...]) when you fetch a
call directly — see Calls.
Retrieve one recording
GET /v1/recordings/{id}
Returns a single Recording. Unknown or non-owned ids return 404.
Downloading the audio
download_url points at the stored audio file. It is time-limited
and signed — fetch it fresh from the API rather than caching it
long-term, because a stale URL will stop resolving once its signature
window passes.
# 1. Get the recording (fresh signed URL)
curl https://api.voepy.com/v1/recordings/voepy_rec_… \
-H "Authorization: Bearer $VOEPY_KEY"
# 2. Download the audio from the returned download_url
curl -L -o recording.mp3 "https://media.voepy.com/recordings/voepy_rec_…mp3?sig=…"
Notes:
- The audio host is separate from the API host — you do not send
your API key to
download_url; the signature in the URL authorizes the fetch. Treat the signed URL as a short-lived secret. download_urlcan be empty for a brief moment between the record action and finalization. Wait for therecording.completedwebhook (or re-fetch) before downloading.- If
status != available, there's nothing to download — see status values.
Storage, retention, and deletion
Recordings stay available until their retention window elapses, at
which point status flips to expired and the audio stops being
served. The metadata row persists either way so your call history stays
intact.
To remove a recording yourself:
DELETE /v1/recordings/{id}
This is a soft delete. voepy flips the recording's
statustoarchivedand stops serving the audio. The metadata row is kept on purpose so your historical and billing records line up. There is no endpoint that restores archived audio, so treat the delete as permanent for the file.
DELETE returns the updated Recording (with status: "archived")
and HTTP 200 — not an empty 204. Deleting a recording already in a
failed state returns 409 recording_not_ready (there's no audio to
remove).
Transcription
Recordings and transcription are independent features. voepy does not
auto-transcribe stored recordings. If you want text, use real-time
transcription on the live call (POST /v1/calls/{id}/actions/transcription_start), which streams
call.transcript_chunk webhooks and exposes the full session at GET /v1/calls/{id}/transcription. See Calls.
Webhooks
Two recording events fire to your subscribed endpoint:
| Event | When |
|---|---|
recording.completed | A call or conference recording is stored and ready to download. |
recording.failed | A recording errors before completion. |
recording.completed payload:
{
"event_type": "recording.completed",
"data": {
"call_id": "voepy_call_01HXYZ123abc456def789012abcdef01",
"recording_id": "voepy_rec_01HXYZ123abc456def789012abcdef10",
"channels": "dual",
"format": "mp3",
"duration_seconds": 132,
"download_url": "https://media.voepy.com/recordings/voepy_rec_…mp3?sig=…"
}
}
For a conference recording with no single host call, call_id is an
empty string. recording.failed carries call_id, recording_id, and
a short reason:
{
"event_type": "recording.failed",
"data": {
"call_id": "voepy_call_…",
"recording_id": "voepy_rec_…",
"reason": "storage_unavailable: Recording could not be persisted within retention window."
}
}
recording_id may be empty if no row was ever persisted. Listen for
recording.completed before you try to download, rather than polling.
See Webhooks for signature verification and retries.
Errors
Every error uses the standard envelope:
{
"error": {
"code": "recording_not_ready",
"message": "Recording is not in a downloadable state."
}
}
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_request | Malformed call_id filter or other bad input. |
| 404 | not_found | Recording id is unknown or not owned by your tenant. |
| 409 | recording_not_ready | Action on a recording whose status isn't usable (e.g. deleting a failed recording). |
Full list: Error reference.
Next
- Calls — start and stop call recording, real-time transcription, and AMD.
- Conferences — record a whole room with pause/resume.
- Webhooks — subscribe to
recording.completedand verify the signature.
