Create speech (TTS)
Generate MP3 or PCM speech with a model and voice using the public API.
Last updated on
POST /v1/audio/speech accepts JSON and returns audio bytes. Authenticate with Authorization: Bearer and your workspace API key.
Generate MP3
Choose a model and voice from the Audio catalog. The example IDs below must be available in your deployment. The commands require curl and jq.
export CICORA_API_KEY="sk_..."
export AUDIO_MODEL="speechkit-tts-general"
export AUDIO_VOICE="marina"
jq -n --arg model "$AUDIO_MODEL" --arg voice "$AUDIO_VOICE" '{
model: $model,
voice: $voice,
input: "Hello! This is a short speech sample.",
response_format: "mp3"
}' | curl --fail-with-body --silent --show-error \
https://api.cicora.ai/v1/audio/speech \
-H "Authorization: Bearer $CICORA_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- -D speech.headers -o speech.mp3After a successful HTTP response, play speech.mp3. On an HTTP error, the output file can contain an error body; do not treat it as audio. The response uses audio/mpeg for MP3. JavaScript must read bytes, not call response.json():
import { writeFile } from "node:fs/promises";
const response = await fetch("https://api.cicora.ai/v1/audio/speech", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CICORA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: process.env.AUDIO_MODEL,
voice: process.env.AUDIO_VOICE,
input: "Hello! This is a short speech sample.",
response_format: "mp3",
}),
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
await writeFile("speech.mp3", Buffer.from(await response.arrayBuffer()));Request fields
| Field | Type | Behavior |
|---|---|---|
model | string, required | Exact public Audio model ID supporting speech |
input | string or array, required | Non-empty text, or turns with text, optional voice and instructions; arrays require multi-speaker support |
voice | string | Public voice ID supported by this model; omission requires a compatible default voice |
response_format | mp3 or pcm | Defaults to pcm; select a published output profile |
speed | positive number | Multiplier within the selected profile's minSpeed–maxSpeed range |
instructions | string | Delivery instructions; best effort, omitted on routes without instruction support |
input_references | array | Reference content, only within the model's published reference limits |
provider, session_id, user, trace | objects or strings | Common Audio options |
Check maxInputCharacters and maxInputBytes. Unknown fields and incompatible voice, format or reference combinations are rejected. Delivery instructions are an exception: a route without instruction support can generate speech while ignoring them. Check instructions in the model capabilities before relying on directed tone or emotion.
Voice and output combinations
After saving the catalog to audio-models.json and setting AUDIO_MODEL, inspect its verified voice profiles:
jq --arg model "$AUDIO_MODEL" '
.data[] | select(.id == $model) | .audio.profiles[]
| select(.operation == "speech") | .profiles[]
| {voice, voiceFamily, isDefault, format, sampleRateHz, channels, minSpeed, maxSpeed}
' audio-models.jsonChanging a model can change the voice IDs, speed range and audio geometry. Use the exact public voice ID from a profile. Language support and sample recordings are visible in the cabinet's voice gallery.
PCM is signed 16-bit little-endian raw audio without a WAV header. Read X-Audio-Sample-Rate and X-Audio-Channels to configure playback or wrap it in a container; do not assume a universal 24 kHz rate. These headers also describe MP3 responses. The result uses Cache-Control: no-store.
References and multiple speakers
Models with multiSpeaker: true can accept turns. Reference parts use type: input_audio with input_audio.data or input_audio.url, type: text with text, or type: image_url with image_url.url. There can be at most three recordings, one transcript per recording and one image; a model can set lower limits or disable references. With multiple recordings, each transcript immediately follows its recording. Do not send references to models whose limits are zero.
Errors and interrupted audio
Before audio starts, validation failures return an HTTP error. Once bytes have been sent, a later failure closes the stream; it cannot replace the audio body with a JSON error. A partial file is not proof of a complete generation. See retry and billing behavior.