Skip to content

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 recordingID format: 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.

  1. List recordings for a window (for example yesterday with timeRange=1d, or a fixed startDate/endDate).
  2. For each id, check whether ./archive/{id}.mp3 (or your naming scheme) already exists — skip if present.
  3. Download missing files with GET .../recordings/{id}.
  4. Advance pagination with startKey until next_start_key is empty.
  5. 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

  1. Sign in as an account administrator.
  2. Open Configuration → Storage → Recordings (/configuration/storage/recordings).
  3. Select a provider (SFTP, Amazon S3, Google Drive, SharePoint, and others depending on your deployment).
  4. Enter credentials and a base folder if required.
  5. 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.


For help with your deployment, contact info@fonouc.com.