The Speech Generation endpoints power the Generation tab in PeachDesk Voice Studio. You send text plus a voice profile, and the Voice Engine synthesizes audio using a model that runs on PeachDesk’s GPU infrastructure — there is nothing to download or install. Generated takes are persisted to your account’s history, where they can be replayed, favorited, exported, or re-rendered as versioned takes.
All endpoints are exposed through the PeachDesk API under the base path /api/voice. For example, the synthesis endpoint POST /generate is called as POST /api/voice/generate. Every request must include your PeachDesk API key in the X-API-Key header (an interactive session token in Authorization: Bearer also works — see Authentication):

Concepts

Voice profile

Every generation is spoken by a voice profile (profile_id) — either a Built-in voice or a Clone from audio profile you created in the Voices tab.

Engine

Pick one of the four served VAANI engines (vaani, vaani-english, vaani-multilingual, vaani-indic). Models are hosted and served by PeachDesk — you just select one from the dropdown.

Asynchronous by default

POST /generate returns immediately with a generating status. Watch progress over Server-Sent Events, then fetch the finished audio.

Versioned takes

Re-rendering or applying effects produces additional versions on the same generation. The default version is what /audio/{id} serves.

Generate speech

POST /api/voice/generate

Queue a text-to-speech job. Returns the generation record right away with status: "generating"; the audio is rendered in the background.
string
required
ID of the voice profile to speak with (Built-in or cloned).
string
required
The text to synthesize. 1–50,000 characters. Long text is automatically split into chunks and stitched together.
string
default:"en"
BCP-style language code. Supported values include en, zh, ja, ko, de, fr, es, it, pt, ru, hi, ar, and many more.
string
default:"vaani"
Synthesis engine. One of vaani (VAANI), vaani-english (VAANI-ENGLISH), vaani-multilingual (VAANI-MULTILINGUAL), or vaani-indic (VAANI-INDIC). Any other value is rejected with 422. If omitted, the profile’s default engine is used; every served engine can clone, so a cloned profile is only re-routed when its recorded engine is unavailable.
string
Legacy field. No served engine offers multiple sizes, so this is accepted and ignored. Omit it.
integer
Optional fixed random seed for reproducible output.
string
Legacy field (up to 500 characters). Stored on the generation record, but no served engine acts on it — the engine that supported instructed generation is no longer in the catalog. Use audio_params to steer delivery.
boolean
default:"false"
When true and the profile has a personality prompt, the input text is rewritten in-character by the LLM before being spoken.
boolean
default:"true"
Normalize the output audio’s loudness.
integer
default:"800"
Maximum characters per chunk when splitting long text (100–5000).
integer
default:"50"
Crossfade duration in milliseconds between stitched chunks (0–500; 0 is a hard cut).
array
Optional list of effect configs applied after synthesis as a versioned take. Overrides the profile’s default effects chain.
object
Fine-tune knobs. See Audio parameters below for the two groups, their ranges, and which engine honors which.

Audio parameters

audio_params holds two kinds of field. Native knobs are passed into the model at synthesis time, and each engine only honors the ones it advertises — sending an unsupported one is safe, it is simply dropped. Universal knobs are applied to the rendered audio afterwards, so they behave identically on every engine. Native knobs: stability, similarity, and style are the 0–1 abstractions; temperature, cfg_weight, and exaggeration are the same controls at their native scale and take precedence when both are sent. Universal knobs (post-processing — every engine):
speed and pitch are post-processing, not model prosody: no VAANI engine takes a speed or pitch parameter natively, so these are applied to the rendered waveform. There is no emotion field — use style/exaggeration for expressiveness.
Response — GenerationResponse
string
Unique generation ID. Use it to poll status and fetch audio.
string
Voice profile used.
string
The final spoken text (after any personality rewrite).
string
Language code.
string
generating, loading_model, completed, or failed.
string | null
Storage path of the rendered audio (populated once complete).
number | null
Audio length in seconds (once complete).
string
Engine that served the request.
string | null
Model size used, if applicable.
integer | null
Seed used.
string
Origin of the request (e.g. manual, personality_speak).
boolean
Whether the generation is favorited.
string
ISO-8601 creation timestamp.
array | null
List of versioned takes once they exist.
string | null
ID of the default version served by /audio/{id}.

POST /api/voice/generate/warmup

Preload an engine on PeachDesk’s servers so the next generation starts warm. The Voice Studio UI calls this when you pick an engine. It never triggers a download — if the model isn’t already resident it is a no-op.
string
default:"vaani"
Engine to preload.
string
Legacy field — no served engine has multiple sizes. Omit it.
Response
Possible status values: warming, already_loaded, or not_downloaded.

GET /api/voice/generate/{generation_id}/status

Stream live status for a generation over Server-Sent Events (text/event-stream). The stream closes once the generation reaches completed or failed.
string
required
The generation ID returned by /generate.
Each data: event carries:
string
Generation ID.
string
loading_model, generating, completed, failed, or not_found.
number | null
Final duration when complete.
string | null
Error message if the generation failed.
string
Origin of the request.

POST /api/voice/generate/{generation_id}/retry

Retry a failed generation with its original parameters. Returns a 400 if the generation is not in failed status.
string
required
ID of the failed generation.
Returns the updated GenerationResponse (status back to generating).

POST /api/voice/generate/{generation_id}/regenerate

Re-run TTS with the same parameters and save the result as a new version on the same generation. The generation must be completed.
string
required
ID of a completed generation.
Returns the updated GenerationResponse.

POST /api/voice/generate/{generation_id}/cancel

Cancel a queued or in-flight generation. Only generations in loading_model or generating can be cancelled.
string
required
ID of an active generation.
Response
Other messages: Queued generation cancelled, Orphaned generation cleared.

Streaming synthesis

These endpoints synthesize and return audio in the same request — useful for low-latency playback.

POST /api/voice/generate/stream

Generate speech and stream the rendered audio back directly without persisting it to history. Accepts the same body as POST /generate.
  • Response: binary audio stream. Content-Type is audio/wav by default (or audio/ogg / audio/flac if requested via audio_params.output_format).
  • Content-Disposition: attachment; filename="speech.wav".

POST /api/voice/generate/stream-live

Progressive, lowest-latency synthesis. Each chunk’s audio is streamed as soon as it’s generated, so the client can start playback on the first sentence while the rest renders. The assembled result is then persisted so it still appears in History.
Wire format: a sequence of frames, each = a 4-byte big-endian length prefix followed by a self-contained WAV blob (directly decodable by the browser’s decodeAudioData). Content-Type: application/octet-stream. The persisted generation ID is returned in the X-Generation-Id response header.
Accepts the same body as POST /generate.

POST /api/voice/generate/import

Register an external audio file as a generation row — used by the Stories timeline to drop in music or other non-TTS audio. The row attaches to a singleton “Imported Audio” profile so it flows through the normal generation plumbing.
file
required
multipart/form-data upload. Allowed extensions: .wav, .mp3, .flac, .ogg, .m4a, .aac, .webm. Maximum 200 MB.
Returns a GenerationResponse with status: "completed", engine: "import", and source: "import".

Generation history

GET /api/voice/history

List your generation history with optional filters.
string
Filter to a single voice profile.
Free-text search over generation text.
integer
default:"50"
Page size.
integer
default:"0"
Pagination offset.
Response — HistoryListResponse
array
List of HistoryResponse entries (each includes the resolved profile_name).
integer
Total number of matching generations.

GET /api/voice/history/stats

Aggregate statistics about your generations (counts, totals).

GET /api/voice/history/{generation_id}

Fetch a single generation by ID, including its profile_name.
string
required
The generation ID.
Response — HistoryResponse
string
Generation ID.
string
Voice profile ID.
string
Display name of the voice profile.
string
Spoken text.
string
Language code.
string | null
Storage path of the audio.
number | null
Length in seconds.
string
Engine used.
string | null
Model size used.
string
Generation status.
boolean
Favorite flag.
string
ISO-8601 timestamp.

POST /api/voice/history/{generation_id}/favorite

Toggle the favorite flag on a generation.
string
required
The generation ID.

DELETE /api/voice/history/{generation_id}

Delete a single generation.
string
required
The generation ID.

DELETE /api/voice/history/failed

Bulk-delete every generation with status: "failed" — backs the Clear failed button in the UI.

POST /api/voice/history/import

Import a previously exported generation from a ZIP archive (multipart/form-data, max 50 MB).
file
required
The .zip archive to import.

GET /api/voice/history/{generation_id}/export

Download a generation as a ZIP archive (audio plus metadata).
string
required
The generation ID.
  • Response: application/zip attachment.

GET /api/voice/history/{generation_id}/export-audio

Download only the audio file from a generation.
string
required
The generation ID.
  • Response: audio/wav attachment. Returns 404 if the generation has no audio.

Serving audio

These endpoints stream stored audio bytes back to the client. The Content-Type is derived from the file extension, so imported .mp3 / .m4a / .ogg files keep their original format.

GET /api/voice/audio/{generation_id}

Serve the default version of a generation’s audio.
string
required
The generation ID.

GET /api/voice/audio/version/{version_id}

Serve the audio for a specific version (take) of a generation — used after regenerating or applying effects.
string
required
The version ID.

GET /api/voice/samples/{sample_id}

Serve a voice profile’s sample audio clip (the reference used by Clone from audio profiles).
string
required
The profile sample ID.
  • Response: audio/wav file. Returns 404 if the sample or its file is missing.
A typical end-to-end flow: POST /generate to queue the job → subscribe to GET /generate/{id}/status for completion → GET /audio/{id} to play or download the result.