Download Call Recordings¶
This guide explains how to archive QVOICE Platform call recording files onto your own systems — for compliance, backup, or integration with internal applications.
You can use either:
- Pull (API) — Your application lists recordings and downloads audio files over HTTPS.
- Push (external storage) — The platform uploads each recording to your SFTP server, S3 bucket, or cloud drive after the call.
flowchart LR
subgraph pull [Pull API]
App[Customer app] -->|list then download| PortalAPI["Portal API :9443"]
PortalAPI --> Local[Local disk or NAS]
end
subgraph push [Push storage]
Gorec[Recording pipeline] -->|upload after call| Ext[SFTP or S3 or Drive]
end
Prerequisites¶
| Requirement | Pull (API) | Push (storage) |
|---|---|---|
| Account with call recording enabled | Yes | Yes |
HTTPS access to the portal API (https://{{ domain }}:9443) |
Yes | Yes (for API config) |
| API key | Yes | Admin JWT or API key for config |
| Local disk, NAS, or object storage for files you download | Yes | — |
| Destination credentials (SFTP, S3, etc.) | — | Yes |
Note
If your account uses call recording encryption, downloads through the API return decrypted audio. You do not need to decrypt files yourself.
Choose a method¶
| Goal | Recommended method |
|---|---|
| Selective archive, custom naming, or CRM sync | Pull via API |
| Every recording lands automatically on your server or bucket | Push to external storage |
| Mix of both (push for archive + pull for ad-hoc) | Configure push, use pull when needed |
Method A — Pull via API¶
Your application authenticates, lists recordings for a date range, then downloads each audio file.
Authentication¶
Use an API key. See API Key authentication.
Required headers:
X-API-Key: <your_api_key>
X-Account-ID: <your_account_id>
Base URL pattern:
https://{{ domain }}:9443/api/v2
1. List recordings (metadata)¶
GET /api/v2/reports/recordings
Provide a date range using one of:
| Params | Description |
|---|---|
timeRange |
Relative window, e.g. 1d, 1w, 1M |
startDate + endDate |
Unix timestamps; maximum 31 days apart; endDate must be greater than startDate |
Pagination:
| Param | Description |
|---|---|
pageSize |
Page size (must be greater than 0) |
startKey |
Cursor from the previous response next_start_key |
Example:
curl -sS "https://{{ domain }}:9443/api/v2/reports/recordings?timeRange=1d&pageSize=50" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID"
Response (shape):
{
"recordings": [
{
"id": "202507-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
}
],
"next_start_key": ""
}
The list returns metadata only (no audio bytes). Use each recording id in the download step.
2. Download audio by recording ID¶
GET /api/v2/reports/recordings/{recordingID}
- Path parameter
recordingIDformat:YYYYMM-{uuid} - Response: raw audio (for example
audio/mpeg) - Header
Content-Disposition: attachment; filename=...
Example — save to a local file:
RECORDING_ID="202507-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
curl -sS -OJ \
"https://{{ domain }}:9443/api/v2/reports/recordings/${RECORDING_ID}" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID"
-OJ uses the server-provided filename. You can also choose your own path with -o:
curl -sS -o "./archive/${RECORDING_ID}.mp3" \
"https://{{ domain }}:9443/api/v2/reports/recordings/${RECORDING_ID}" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID"
3. Get recordings by call ID¶
GET /api/v2/reports/recordings/by_call_id/{call_id}
This endpoint returns JSON, not a raw file. Each item includes base64-encoded audio in data:
{
"recordings": [
{
"id": "202507-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"name": "recording.mp3",
"content_type": "audio/mpeg",
"data": "<base64>",
"size": 1234567
}
],
"total": 1
}
Use this when you already have a call ID (for example from a CDR or CRM). Prefer download-by-recording-ID when you want a simple binary file write.
Recommended local archive pattern¶
- List recordings for a window (for example yesterday with
timeRange=1d, or a fixedstartDate/endDate). - For each
id, check whether./archive/{id}.mp3(or your naming scheme) already exists — skip if present. - Download missing files with
GET .../recordings/{id}. - Advance pagination with
startKeyuntilnext_start_keyis empty. - Schedule the job (cron, systemd timer, or orchestrator) to run periodically.
Tip
Keep list windows at or under 31 days when using startDate/endDate. Walk month by month for longer backfills.
Method B — Push to external storage¶
Configure the account so the recording pipeline uploads files to your SFTP server, S3 (or S3-compatible) bucket, or connected cloud drive after each call. You do not need to poll the API for every file.
Portal UI¶
- Sign in as an account administrator.
- Open Configuration → Storage → Recordings (
/configuration/storage/recordings). - Select a provider (SFTP, Amazon S3, Google Drive, SharePoint, and others depending on your deployment).
- Enter credentials and a base folder if required.
- Save — the platform runs a connection test before accepting the configuration.
After a successful save, new recordings are uploaded automatically.
Storage API¶
Admin-authenticated requests:
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v2/config/storage/recordings |
Read current config |
PUT |
/api/v2/config/storage/recordings |
Create or update config (204 on success) |
DELETE |
/api/v2/config/storage/recordings |
Remove config |
Use the same auth headers as other portal APIs (Authorization: Bearer … or X-API-Key + X-Account-ID).
SFTP — what you must provide¶
| Field | JSON key | Required |
|---|---|---|
| Host | auth.sftp.url |
Yes |
| Port | auth.sftp.port |
Yes |
| Username | auth.sftp.username |
Yes |
| Password | auth.sftp.password |
Yes |
| Base folder | options.recordings.directory |
Recommended |
Example PUT body:
{
"provider": "sftp",
"local_storage": false,
"auth": {
"sftp": {
"url": "sftp.example.com",
"port": 22,
"username": "recordings",
"password": "REPLACE_ME"
}
},
"options": {
"recordings": {
"directory": "/incoming/call-recordings",
"date_directory_disabled": false
}
}
}
curl -sS -X PUT "https://{{ domain }}:9443/api/v2/config/storage/recordings" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID" \
-H "Content-Type: application/json" \
-d @sftp-recordings-storage.json
S3 (Amazon or S3-compatible) — what you must provide¶
Provider value: amazon.
| Field | JSON key | Required |
|---|---|---|
| Access key ID | auth.amazon.access_key |
Yes |
| Secret access key | auth.amazon.secret_access_key |
Yes |
| Bucket | auth.amazon.bucket |
Yes |
| Region | auth.amazon.region |
Yes |
| Custom endpoint (MinIO, etc.) | auth.amazon.endpoint |
Optional |
| Base folder / prefix | options.recordings.directory |
Recommended |
Example PUT body:
{
"provider": "amazon",
"local_storage": false,
"auth": {
"amazon": {
"access_key": "AKIA...",
"secret_access_key": "REPLACE_ME",
"bucket": "my-call-recordings",
"region": "us-east-1"
}
},
"options": {
"recordings": {
"directory": "archive",
"date_directory_disabled": false
}
}
}
When date_directory_disabled is false (default), uploads typically use date-based subfolders under the base directory.
Other providers¶
Google Drive and Microsoft SharePoint are available on deployments that have the corresponding OAuth application configured. Connect them from Configuration → Storage → Recordings (OAuth consent flow). Deep OAuth setup is outside the scope of this guide.
Security and retention¶
- Always use HTTPS. Do not send API keys or storage passwords over plain HTTP.
- Store API keys and storage secrets in a secrets manager; rotate them periodically.
- Do not commit credentials into source control or documentation.
- If a recording was removed by your account retention policy, download requests return 404 with a retention message. Archive via pull or push before retention deletes media if you need long-term copies.
- Encrypted-at-rest recordings are decrypted for authorized API downloads. See Call Recording encryption.
Warning
Tag or queue restrictions on portal users can return 403 for individual downloads. API keys used for server-to-server archive typically operate with admin-level access for the account — still protect the key as a high-privilege credential.
Related docs¶
For help with your deployment, contact info@fonouc.com.