curl --request POST \
--url https://api.vivix.ai/v1/voices/design \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data @- <<EOF
{
"instruction": "A mature male news anchor with a deep, steady voice",
"sample_text": "Welcome to tonight's broadcast.",
"language": "zh",
"name": "Studio Anchor"
}
EOFimport requests
url = "https://api.vivix.ai/v1/voices/design"
payload = {
"instruction": "A mature male news anchor with a deep, steady voice",
"sample_text": "Welcome to tonight's broadcast.",
"language": "zh",
"name": "Studio Anchor"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
instruction: 'A mature male news anchor with a deep, steady voice',
sample_text: 'Welcome to tonight\'s broadcast.',
language: 'zh',
name: 'Studio Anchor'
})
};
fetch('https://api.vivix.ai/v1/voices/design', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.vivix.ai/v1/voices/design",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'instruction' => 'A mature male news anchor with a deep, steady voice',
'sample_text' => 'Welcome to tonight\'s broadcast.',
'language' => 'zh',
'name' => 'Studio Anchor'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.vivix.ai/v1/voices/design"
payload := strings.NewReader("{\n \"instruction\": \"A mature male news anchor with a deep, steady voice\",\n \"sample_text\": \"Welcome to tonight's broadcast.\",\n \"language\": \"zh\",\n \"name\": \"Studio Anchor\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.vivix.ai/v1/voices/design")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"instruction\": \"A mature male news anchor with a deep, steady voice\",\n \"sample_text\": \"Welcome to tonight's broadcast.\",\n \"language\": \"zh\",\n \"name\": \"Studio Anchor\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.vivix.ai/v1/voices/design")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"instruction\": \"A mature male news anchor with a deep, steady voice\",\n \"sample_text\": \"Welcome to tonight's broadcast.\",\n \"language\": \"zh\",\n \"name\": \"Studio Anchor\"\n}"
response = http.request(request)
puts response.read_body{
"code": 0,
"message": "success",
"data": {
"voice_id": "voice_8f2c1a4e-5d67-4b03-a9f1-2e6c7b0d5a94",
"name": "Studio Anchor",
"language": "zh",
"preview_audio_url": "https://static.vivi-x.ai/voice_preview/2026/10/13/8f2c1a4e5d674b03/a9f12e6c7b0d5a94.wav"
}
}Design Voice
Creates a voice from a text description, with no reference audio. Pass the returned voice_id as pipeline_config.tts_config.tts_voice_id when you start a session.
curl --request POST \
--url https://api.vivix.ai/v1/voices/design \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data @- <<EOF
{
"instruction": "A mature male news anchor with a deep, steady voice",
"sample_text": "Welcome to tonight's broadcast.",
"language": "zh",
"name": "Studio Anchor"
}
EOFimport requests
url = "https://api.vivix.ai/v1/voices/design"
payload = {
"instruction": "A mature male news anchor with a deep, steady voice",
"sample_text": "Welcome to tonight's broadcast.",
"language": "zh",
"name": "Studio Anchor"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
instruction: 'A mature male news anchor with a deep, steady voice',
sample_text: 'Welcome to tonight\'s broadcast.',
language: 'zh',
name: 'Studio Anchor'
})
};
fetch('https://api.vivix.ai/v1/voices/design', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.vivix.ai/v1/voices/design",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'instruction' => 'A mature male news anchor with a deep, steady voice',
'sample_text' => 'Welcome to tonight\'s broadcast.',
'language' => 'zh',
'name' => 'Studio Anchor'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.vivix.ai/v1/voices/design"
payload := strings.NewReader("{\n \"instruction\": \"A mature male news anchor with a deep, steady voice\",\n \"sample_text\": \"Welcome to tonight's broadcast.\",\n \"language\": \"zh\",\n \"name\": \"Studio Anchor\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.vivix.ai/v1/voices/design")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"instruction\": \"A mature male news anchor with a deep, steady voice\",\n \"sample_text\": \"Welcome to tonight's broadcast.\",\n \"language\": \"zh\",\n \"name\": \"Studio Anchor\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.vivix.ai/v1/voices/design")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"instruction\": \"A mature male news anchor with a deep, steady voice\",\n \"sample_text\": \"Welcome to tonight's broadcast.\",\n \"language\": \"zh\",\n \"name\": \"Studio Anchor\"\n}"
response = http.request(request)
puts response.read_body{
"code": 0,
"message": "success",
"data": {
"voice_id": "voice_8f2c1a4e-5d67-4b03-a9f1-2e6c7b0d5a94",
"name": "Studio Anchor",
"language": "zh",
"preview_audio_url": "https://static.vivi-x.ai/voice_preview/2026/10/13/8f2c1a4e5d674b03/a9f12e6c7b0d5a94.wav"
}
}Use the voice when you create a session
Use the voice when you create a session
{
"pipeline_config": {
"tts_config": {
"tts_voice_id": "voice_8f2c1a4e"
}
}
}
tts_voice_id is required: the service fills tts_provider / tts_model_id for that voice.Is this the same as Clone Voice?
Is this the same as Clone Voice?
| Clone Voice | Design Voice | |
|---|---|---|
| Input | Reference audio (file_id) | Text description (instruction) |
| You need to record audio first | Yes | No |
| Extra response field | — | preview_audio_url |
Billing against max_voices | Yes | Yes, same quota |
max_voices limit as a cloned one.Writing the instruction
Writing the instruction
instruction describes the voice, not the content. Keep it concrete and about delivery:A warm female narrator in her thirties, slightly husky, unhurried pace, gentle emphasis on the ends of sentences.
Preview audio
Preview audio
preview_audio_url is a permanent, unauthenticated WAV (mono, 16 kHz) you can play directly in an <audio> element or download.The field is absent when the preview could not be generated. A missing preview does not mean the voice failed — the voice is still usable for sessions. There is no status field to poll: the response itself means the voice is ready.Errors
Errors
{code, message, data} envelope; a non-zero code identifies the error.| Case | Error code |
|---|---|
Missing instruction or name | 20003 missing required parameter |
Missing sample_text for a language other than zh / en | 20003 missing required parameter |
instruction or sample_text exceeds 1500 characters | 20004 invalid parameter |
name exceeds 100 characters | 20004 invalid parameter |
language is invalid | 20004 invalid parameter |
The voice quota (max_voices) is reached (HTTP 403) | 30010 voice clone quota exceeded |
| The design provider call failed (HTTP 502) | 30007 voice clone provider failed |
| The API key is missing or invalid | 10001 missing api key or 10003 invalid api key |
Authorizations
Your Vivix API key. Keep it on your server.
Body
Describes the voice to synthesize: timbre, age, speaking style, pace. At most 1500 characters. Required.
Display name, at most 100 characters. Required.
Text the voice speaks in the generated preview. At most 1500 characters. Required when language is neither Chinese nor English; optional for zh and en, where the service supplies a default.
Language of sample_text: zh (Chinese), en (English), fr (French), de (German), ja (Japanese), ko (Korean), ru (Russian), pt (Portuguese), th (Thai), id (Indonesian), vi (Vietnamese), it (Italian), es (Spanish), ms (Malay), fil (Filipino), ar (Arabic).