# Create speech (TTS)

Source: https://cicora.ai/en/docs/audio-speech

`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](/en/docs/audio). The example IDs below must be available in your deployment. The commands require `curl` and `jq`.

```bash
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.mp3
```

After 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()`:

```javascript
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](/en/docs/audio#compatibility-and-optional-features)                                      |

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:

```bash
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.json
```

Changing 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](/en/docs/audio#requests-jobs-and-retries).

## Troubleshooting


**The file does not play**


Check the HTTP status and Content-Type first. Raw PCM needs the sample rate and channel count from the response headers; it is not an MP3 or WAV file.


**A voice or speed is rejected, or instructions have no effect**


Refresh the selected model's profiles and choose a compatible combination. Instructions are best effort; a model without instruction support ignores them.

## FAQ

### What is cicora.ai?

cicora.ai is a multi-model AI workspace and compatible API for chat, text, images, video, coding tools, and teams. Start with the [overview](/en.md), [documentation](/en/docs.md), or [model catalog](/en/models.md).

### How is usage billed?

Confirmed usage is charged in USD from the active workspace's prepaid balance. Use the live catalog for current model prices and the [usage guide](/en/docs/usage-costs.md) for settlement details.

### Which protocols and integrations are available?

Documentation covers OpenAI-compatible Chat Completions and Responses, Anthropic Messages, image interfaces, Claude Code, OpenCode, and Codex CLI. Compatibility does not imply support for every upstream parameter.

### Which sources are authoritative and current?

For model IDs, availability, capabilities, limits, and prices, use the [live catalog](/en/models.md). For API behavior, use the matching [documentation page](/en/docs.md). Never include API keys, private workspace data, or preview URLs in public documents.
