Text-to-Speech

Turn text into spoken audio using the OpenAI Audio API format. One synchronous call: send the text, receive the whole MP3 back. Uses the same API key as the chat endpoints.

Base URL: https://api.techopenclaw.com — authentication uses the same Authorization: Bearer toc-..... header as the chat endpoints (details in API Reference).

POST/v1/audio/speechOpenAI

Synthesize speech. The response body is the raw audio file (Content-Type: audio/mpeg), not JSON — save it straight to disk.

FieldTypeRequiredDescription
modelstringYesTTS model name from the LLM Catalog. Replace tts-avevoice-1.5-max in the examples with a speech model available to your account.
inputstringYesThe text to speak, 1–4096 characters. Characters are also the billing unit — longer input costs more, nothing else does.
voicestring / objectYesThe voice to use — what counts as a valid value depends on the model family (see Models & voices below): tts-avevoice* models take a voice name (e.g. Aanya), tts-eleven* models take a voice id (e.g. elevenlabs_EXAVITQu4vr4xnSDxMaL). A custom voice can also be passed as { "id": "voice_..." } — including your own cloned voice id.
response_formatstringNomp3 (the default). Other formats are not supported — the provider rejects them with 400. Streaming is not supported either — the audio comes back whole, never as SSE.
speednumberNoPlayback speed, 0.25–4.0 (default 1.0). Forwarded to the engine; an explicit settings.speed wins over it.
languagestringDependsLanguage for the synthesis engine (e.g. vi, en). An extension beyond the OpenAI format. Required by tts-avevoice and tts-avevoice-fast; optional for every other speech model. If a language is unavailable for a model, the error message names it.
settingsobjectNoEngine-specific settings, forwarded to the engine as-is. Use it when the engine has knobs the OpenAI format does not carry.
Request — curl (GenVoice 1.5, no language needed)
curl https://api.techopenclaw.com/v1/audio/speech \
  -H "Authorization: Bearer toc-....." \
  -H "Content-Type: application/json" \
  -d '{
  "model": "tts-avevoice-1.5-max",
  "input": "Chào mừng bạn đến với Techopenclaw.",
  "voice": "Aanya"
}' \
  --output speech.mp3
Request — curl (tts-avevoice / tts-avevoice-fast need language)
curl https://api.techopenclaw.com/v1/audio/speech \
  -H "Authorization: Bearer toc-....." \
  -H "Content-Type: application/json" \
  -d '{
  "model": "tts-avevoice",
  "input": "Xin chào, đây là giọng đọc tiếng Việt.",
  "voice": "Aanya",
  "language": "vi"
}' \
  --output speech.mp3
Request — curl (Eleven models use voice ids)
curl https://api.techopenclaw.com/v1/audio/speech \
  -H "Authorization: Bearer toc-....." \
  -H "Content-Type: application/json" \
  -d '{
  "model": "tts-eleven-v4",
  "input": "Chào mừng bạn đến với Techopenclaw.",
  "voice": "elevenlabs_Jxl0M2KOcLjO5ReRA5rH"
}' \
  --output speech.mp3

Works with the OpenAI SDK by pointing base_url at Techopenclaw:

Request — Python (openai SDK)
from openai import OpenAI

client = OpenAI(api_key="toc-.....", base_url="https://api.techopenclaw.com/v1")

with client.audio.speech.with_streaming_response.create(
    model="tts-avevoice-1.5-max",
    voice="Aanya",
    input="Chào mừng bạn đến với Techopenclaw.",
) as response:
    response.stream_to_file("speech.mp3")

Models & voices

What a valid voice looks like — and whether language is required — depends on the model family:

Modelsvoice valuelanguage
tts-avevoice, tts-avevoice-fastVoice name — e.g. AanyaRequired (e.g. "vi", "en")
tts-avevoice-1.5-max, tts-avevoice-1.5-miniVoice name — e.g. AanyaOptional
All tts-eleven-* modelsVoice id — e.g. elevenlabs_EXAVITQu4vr4xnSDxMaLOptional

Voice examples per family (the catalogues live on the provider side and can change). To speak with your own voice instead, clone it from a sample:

voiceLanguage
AanyaHindi (hi-IN)
AaravHindi (hi-IN)
AbbyEnglish US
AdityaEnglish IN
AlistairEnglish GB
AlainFrench (fr-FR)
AlvaroSpanish (es-ES)
CamilaSpanish (es-MX)
AnnikaGerman (de-DE)
AlyonaRussian (ru-RU)
AsukaJapanese (ja-JP)
BeatrizPortuguese BR
AndoyEnglish PH
AmaraEnglish NG
voiceVoice
elevenlabs_Jxl0M2KOcLjO5ReRA5rHDat — Northern Vietnamese
elevenlabs_njal9ZYZB2BSvEp2g2FeCuong Nguyen — Calm Narrator (Vietnamese)
elevenlabs_EXAVITQu4vr4xnSDxMaLSarah — Mature, Reassuring
elevenlabs_pNInz6obpgDQGcFmaJgBAdam — Deep
elevenlabs_cgSgspJ2msm6clMCkdW9Eric — Smooth, Trustworthy
elevenlabs_onwK4e9ZLuTAKqWW03F9Daniel — Steady Broadcaster
elevenlabs_JBFqnCBsd6RMkjVDRZzbGeorge — Storyteller
elevenlabs_IKne3meq5aSn9XLyUdCDCharlie — Deep, Confident
elevenlabs_pFZP5JQG7iQjIQuC4BkuLily — Velvety Actress
elevenlabs_SAz9YHcvj6GT2YYXdXwwRiver — Relaxed, Neutral
elevenlabs_Xb7hH8MSUJpSbSDYk0k2Alice — Educator
elevenlabs_N2lVS1w4EtoT3dr4eOWOCallum — Husky Trickster

Voice cloning

Upload a short, clean sample of a voice and get back a voice id you can speak with anywhere a voice name is accepted. A clone belongs to the API key that created it — other keys never see it. Available on the tts-avevoice* models; tts-eleven-* models cannot clone.

POST/v1/audio/voicesOpenAI

Clone a voice from an audio sample. multipart/form-data only — JSON is rejected. Returns the new voice with its id.

FieldTypeRequiredDescription
modelstringYesA speech model that supports cloning: tts-avevoice, tts-avevoice-fast, tts-avevoice-1.5-max, tts-avevoice-1.5-mini. Cloned voices made on one model work across these four. tts-eleven-* models cannot clone (402).
namestringYesA label for the cloned voice, up to 64 characters. Shown when you list your clones.
languagestringYesThe language of the sample and the voice (e.g. vi-VN, en). Always required for cloning — a request without it is rejected with 400.
filefileYesThe audio sample: mp3 or wav, at most 4 MB, roughly 10–30 seconds of one voice speaking clearly with no background music.
Request — curl (clone a Vietnamese voice)
curl https://api.techopenclaw.com/v1/audio/voices \
  -H "Authorization: Bearer toc-....." \
  -F "model=tts-avevoice" \
  -F "name=my-voice" \
  -F "language=vi-VN" \
  -F "[email protected];type=audio/mpeg"
Response — 201 Created
{
  "id": "6214a0dd-076e-4508-bfe3-f2328f5bb653",
  "object": "voice",
  "name": "my-voice",
  "status": "processing",
  "model": "tts-avevoice",
  "owned": true,
  "created_at": 1791253089
}
Request — Python (requests)
import requests

resp = requests.post(
    "https://api.techopenclaw.com/v1/audio/voices",
    headers={"Authorization": "Bearer toc-....."},
    data={"model": "tts-avevoice", "name": "my-voice", "language": "vi-VN"},
    files={"file": ("sample.mp3", open("sample.mp3", "rb"), "audio/mpeg")},
)
voice_id = resp.json()["id"]
GET/v1/audio/voices

List voices. With ?type=clone you get only the clones your key owns; without it, the system voice catalogue of the provider.

Request — curl (your clones)
curl "https://api.techopenclaw.com/v1/audio/voices?type=clone" \
  -H "Authorization: Bearer toc-....."
Response — 200 OK
{
  "object": "list",
  "data": [
    {
      "id": "6214a0dd-076e-4508-bfe3-f2328f5bb653",
      "object": "voice",
      "name": "my-voice",
      "type": "clone",
      "models": ["tts-avevoice"],
      "owned": true,
      "status": "processing"
    }
  ],
  "page": 1,
  "page_size": 1,
  "has_more": false,
  "complete": true
}
DELETE/v1/audio/voices/{voice_id}

Delete one of your clones. Deleting a voice that belongs to another key returns 404.

Request — curl
curl -X DELETE "https://api.techopenclaw.com/v1/audio/voices/6214a0dd-076e-4508-bfe3-f2328f5bb653" \
  -H "Authorization: Bearer toc-....."
Response — 200 OK
{
  "id": "6214a0dd-076e-4508-bfe3-f2328f5bb653",
  "object": "voice",
  "deleted": true
}

Speak with the clone by passing its id as the voice object:

Request — curl (speech with the cloned voice)
curl https://api.techopenclaw.com/v1/audio/speech \
  -H "Authorization: Bearer toc-....." \
  -H "Content-Type: application/json" \
  -d '{
  "model": "tts-avevoice",
  "input": "Đây là giọng nói đã được nhân bản từ mẫu âm gốc.",
  "voice": { "id": "6214a0dd-076e-4508-bfe3-f2328f5bb653" },
  "language": "vi"
}' \
  --output speech.mp3
RuleDetail
Content typemultipart/form-data — a JSON body is rejected with 400. Send it like a file upload form, not a JSON payload.
OwnershipA clone belongs to the API key that created it. Other keys cannot list, speak with, or delete it — they get 404 as if the voice did not exist.
Ready stateA fresh clone returns status: "processing". It becomes usable once the engine finishes — usually within a minute; speaking with it before then either succeeds or fails un-billed, safe to retry.
LimitsSample up to 4 MB; name up to 64 characters; input text when speaking: 1–4096 characters as usual.

Voice-clone billing

ActionBilling
Create a clone (201)Charged once: the model's flat credits-per-clone rate (see Pricing Policy). A failed create is not charged.
Speak with a cloned voiceSame per-character rate as any other speech on that model — no extra fee for using a clone.
List clones / delete a cloneFree.

Voice-clone error codes

CodeMeaningAction
400Not multipart, a missing model/name/language/file, a name over 64 characters, or a sample over 4 MBRead error.message — it names the offending field. language is required for cloning even on models where it is optional for speech
401Invalid or missing API keyCheck your toc- key
402Not enough credits for the clone price, or the model does not support cloningTop up, or switch to one of the tts-avevoice* models
403Money-wallet keys cannot clone voicesUse a credit key
404Unknown model, model is not a speech model, or the voice id belongs to another key / does not existVerify the model in the LLM Catalog; only list and use clones created by your own key
529The cloning engine is busy or failedRetry — respect Retry-After when present. A failed create is not charged

Billing

Speech is priced per character: price = the model's credits-per-character rate × the number of characters in input. Rates per model are shown in the Pricing Policy. You are charged only after the audio is delivered.

OutcomeBilling
200Charged: the model's credit-per-character rate × characters in input
404 / 529 — wrong model, or synthesis failed or timed outNot charged

Error Codes

CodeMeaningAction
400Malformed request — missing model/input/voice, input over 4096 characters, a response_format the provider rejects, speed outside 0.25–4.0, or a missing language on a model that requires itRead error.message — it names the offending field
401Invalid or missing API keyCheck your toc- key
402The model has no per-character price configuredIf it persists on a listed model, the combo is missing its price — contact support
404Unknown model, or the model is not configured for speechVerify the model name against the LLM Catalog
422The selected voice was not foundUse a voice id from the voice tables below — plain names only work on tts-avevoice* models
529Provider busy, synthesis failed, or the request timed outRetry — respect Retry-After when present. Failed or timed-out synthesis is not charged
Support · Telegram