Skip to content

Voices + Media files

Two shared resource groups used in AutoCall:

  • Voices — list of available TTS voices to set as voice_id when creating a script.
  • Media files — audio files (.wav/.mp3/...) used as target_audio_url for the DTMF actions playback / playback_then_hangup.
http
GET /api/autocall/voices

Purpose: returns only available voices. These voices are guaranteed to render preview/dial successfully — they will NOT throw Voice not available as can happen with arbitrary voices from a provider.

Response 200 (only the 5 fields the client needs):

json
{
  "data": [
    {
      "id": "voice_id_premium_02",
      "name": "vi-VN-female-01",
      "preview_url": "https://app.zorio.vn/api/autocall/voices/preview",
      "gender": "female",
      "language": "vi"
    }
  ]
}

Response fields:

FieldTypeDescription
idstringVoice ID — used as voice_id when calling POST /api/autocall/scripts
namestringFriendly display name
preview_urlstring | nullMP3 sample URL for preview playback
genderstring | nullmale / female
languagestring | nullvi / en / ...

Fallback when a provider fails

If a provider integration is temporarily unreachable (network/API key), the endpoint still returns the voice with only the id + name=id fields so the client does NOT error out — the UI can fall back to displaying the raw ID.

Tier A vs Tier B

  • Tier A = premium realtime via a premium TTS provider (per-character charge).
  • Tier B = local pre-rendered vi-VN-female-01 (no fee, high throughput). Recommended default for high volume.

tts_tier is configured at the script level (see Scripts). The system auto-routes by voice_id → matching provider.

Media Files (audio assets)

The standard Zorio Media Files module — upload audio files (.wav/.mp3/.m4a/...) and they are auto-converted to WAV 16-bit PCM 8 kHz mono. Inside AutoCall, use the file_path of a media file as target_audio_url for the DTMF actions playback / playback_then_hangup.

Category enum:

  • announcement — announcement for DTMF actions (e.g. "Thank you", "Transferring you to an agent...")
  • ivr — IVR prompts (greeting, menu...)
  • moh — music on hold
  • library — general

Permission

manage_recordings or admin is required to upload/edit/delete; viewers may list and fetch audio.

List media files

http
GET /api/media-files?category=announcement

Query:

ParamTypeDescription
categorystring, optionalFilter by the enum ivr / moh / announcement / library. Omit → return all

Response 200:

json
{
  "data": [
    {
      "id": 20,
      "tenant_id": 1,
      "name": "VNT v2",
      "file_path": "/media/ivr/studio-1776477792_1776477798_d939.wav",
      "file_size": 176718,
      "duration": 11,
      "format": "wav",
      "category": "ivr",
      "tts_text": null,
      "tts_voice": null,
      "tts_provider": null,
      "created_at": "2026-04-18 02:03:19"
    }
  ]
}

Response fields:

FieldTypeDescriptionValid values
data[].idintegerMedia file IDPositive integer
data[].tenant_idintegerID of the owning account
data[].namestringDisplay nameUp to 255 characters
data[].file_pathstringFile identifier (use as target_audio_url) for DTMF action playbackInternal identifier
data[].file_sizeintegerFile size (bytes)> 0
data[].durationintegerAudio duration (seconds)>= 0
data[].formatstringAudio format (always wav after conversion)wav
data[].categorystringFile categoryivr / moh / announcement / library
data[].tts_textstring | nullSource text (only != null if the file was synthesized via TTS)
data[].tts_voicestring | nullVoice ID (only for TTS files)
data[].tts_providerstring | nullTTS provider usedtts_provider_01 / local / null
data[].created_atdatetimeUpload time (UTC, format YYYY-MM-DD HH:mm:ss)

TTS metadata fields

tts_text, tts_voice, tts_provider are only != null when the file was synthesized via TTS (not relevant for manually uploaded files). Informational; integrators do not need to handle them.

Upload a media file

http
POST /api/media-files
Content-Type: multipart/form-data

Body (multipart):

FieldRequiredTypeDescription
fileYesbinaryAudio file (.wav, .mp3, .m4a, .ogg, .aac, .flac). Max 50 MB
nameYesstringDisplay name (e.g. "Thank you message")
categoryNoenumivr / moh / announcement / library. Default library

Sample request (curl):

bash
curl -X POST https://app.zorio.vn/api/media-files \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" \
  -F "file=@thank-you.mp3" \
  -F "name=Thank you message" \
  -F "category=announcement"

Response 201:

json
{
  "data": {
    "tenant_id": 1,
    "name": "Test",
    "file_path": "/media/library/test_1782374777.wav",
    "file_size": 43704,
    "duration": 3,
    "format": "wav",
    "category": "library",
    "id": 21
  },
  "message": "File uploaded and converted successfully."
}

Response fields:

FieldTypeDescriptionValid values
data.idintegerNewly created media file IDPositive integer
data.tenant_idintegerID of the owning account
data.namestringDisplay name
data.file_pathstringFile identifier after conversion (WAV 16-bit PCM 8 kHz mono)Internal identifier
data.file_sizeintegerConverted file size (bytes)> 0
data.durationintegerAudio duration (seconds)>= 0
data.formatstringFormat after conversionAlways wav
data.categorystringFile categoryivr / moh / announcement / library
messagestringSuccess confirmation message

Error 422 (wrong file format / too large):

json
{
  "message": "The file must be a file of type: wav, mp3, m4a, ogg, aac, flac.",
  "errors": { "file": ["The file must be a file of type: wav, mp3, m4a, ogg, aac, flac."] }
}

Error 500 (server missing audio conversion engine):

json
{ "error": "audio conversion engine is not installed on the server" }

Update media file metadata

http
PATCH /api/media-files/{id}

Changes name / category only — the physical file is not touched.

Path params:

FieldTypeRequiredDescriptionValid values
idintegerYesID of the media file to updatePositive integer

Body:

json
{
  "name": "Thank you — version 2",
  "category": "announcement"
}

Body fields:

FieldTypeRequiredDescriptionValid values
namestringNoChange display nameUp to 255 characters
categorystringNoChange categoryivr / moh / announcement / library

Response 200:

json
{
  "data": {
    "id": 21,
    "tenant_id": 1,
    "name": "Thank you — version 2",
    "file_path": "/media/library/test_1782374777.wav",
    "file_size": 43704,
    "duration": 3,
    "format": "wav",
    "category": "announcement",
    "tts_text": null,
    "tts_voice": null,
    "tts_provider": null,
    "created_at": "2026-06-25 08:06:17"
  },
  "message": "Audio file updated."
}

Response fields: same shape as a GET /api/media-files item (see the table in List media files) plus a message next to data.

Replace the physical file (keep the same ID)

http
POST /api/media-files/{id}/replace
Content-Type: multipart/form-data

The old file is backed up as <path>.bak-<timestamp> and the new content is written to the same file_path — IVR/DTMF actions referencing target_audio_url do not need to be updated because the path is unchanged.

Path params:

FieldTypeRequiredDescriptionValid values
idintegerYesID of the media file to replacePositive integer

Body (multipart):

FieldTypeRequiredDescriptionValid values
filebinaryYesNew audio file.wav, .mp3, .m4a, .ogg, .aac, .flac, max 50 MB

Response 200:

json
{
  "data": {
    "id": 21,
    "tenant_id": 1,
    "name": "Thank you — version 2",
    "file_path": "/media/library/test_1782374777.wav",
    "file_size": 43704,
    "duration": 3,
    "format": "wav",
    "category": "announcement",
    "tts_text": null,
    "tts_voice": null,
    "tts_provider": null,
    "created_at": "2026-06-25 08:06:17"
  },
  "backup": "test_1782374777.wav.bak-20260625-080956",
  "message": "File replaced successfully. The previous version has been backed up."
}

Response fields:

FieldTypeDescriptionValid values
dataobjectMedia file object (same shape as a GET /api/media-files item)
backupstringName of the backup file for the previous version. Full path = dirname(file_path) + '/' + backup<filename>.bak-<timestamp>
messagestringConfirmation message

backup field at top level

Not nested under data — it contains the backup file name. Full path = dirname(file_path) + '/' + backup.

Delete a media file

http
DELETE /api/media-files/{id}

Deletes the DB record + physical file.

Response 200:

json
{ "message": "Deleted." }

Audit before deleting

If a DTMF action's target_audio_url still references the file, dialing will fail with "file not found". Audit usage before deleting.

Stream audio (play preview)

http
GET /api/media-files/{id}/audio

Returns a binary WAV stream with Content-Type: audio/wav. Use in an HTML5 <audio> tag or save via curl:

bash
curl -s https://app.zorio.vn/api/media-files/5/audio \
  -H "Authorization: Bearer $TOKEN" \
  -o thank-you.wav

Cấp phép theo điều khoản sử dụng của Zorio.