---
title: "Submit a TTS generation job"
method: POST
path: "/api/v1/tts"
tags: ["Public API v1"]
---

# Submit a TTS generation job

`POST /api/v1/tts`

Submits a text-to-speech job. Returns immediately with a `jobId`. Poll `GET /v1/tts/{jobId}` to check progress and retrieve the audio URL when complete. Tokens are deducted on submission, billed by the submitted character count (minimum 50, × the AI surcharge only when you opt in with `aiRefine: true`), and refunded if the job permanently fails.

## Request body

- PublicTtsRequestDto
  - `text` string, required — Vietnamese text to synthesize.
  - `voiceId` string — Voice ID — must be one returned by GET /v1/voices for the requested engine. Omit to use that engine's default voice.
  - `emotion` 'natural' | 'storytelling' | 'tin_tuc' | 'tu_nhien' | 'doc_truyen' | 'emotion_0' | 'emotion_1' | 'emotion_2' | 'emotion_3' | 'emotion_4' | 'emotion_5' | 'emotion_6' | 'emotion_7' | 'emotion_8' | 'emotion_9' | 'emotion_10' | 'emotion_11' | 'emotion_12' | 'emotion_13' | 'emotion_14' | 'emotion_15' | 'emotion_16' | 'emotion_17' | 'emotion_18' | 'emotion_19'
  - `speed` number — Playback rate of the synthesized speech. 1.0 is the natural pace.
  - `engine` 'v3' | 'v4' — TTS engine: "v3" (48 kHz, default, full feature set) or "v4" (48 kHz, reference-based voices, premium rate). Omit to use the configured default. The voice must belong to the engine you pick.
  - `aiRefine` boolean — AI text refinement. Defaults to FALSE on the public API: the text is synthesized as submitted, with no AI moderation and no pronunciation normalization, billed at the plain per-character rate. Set true to run the same AI step the web app uses — formulas, acronyms and mixed-in English are read correctly and the content is checked — billed with the AI surcharge and one extra model round-trip of latency. Deterministic text preparation is applied either way.

## Response `201`

Job accepted and queued. Poll GET /api/v1/tts/{jobId}.

- PublicTtsSubmitResponseDto
  - `jobId` string, required
  - `status` string, required
  - `message` string, required

## Other responses

- `400` — Unknown voice, voice belonging to another engine, or text too long.
- `401` — API key missing, malformed, or revoked.
- `403` — Your plan does not include this engine or feature, or the grant is out of tokens / expired.
- `422` — Content refused by moderation. Only reachable with `aiRefine: true`.
- `429` — Rate limit or token quota exceeded. When the limit came from the application the response carries `Retry-After` (seconds) and the `X-RateLimit-*` headers and is counted against your API key; a 429 with none of those headers came from the edge proxy and is counted against your source address, shared with every other key calling from it. Back off on either.

---

[API](https://skmtc.dev/vieneu/apis/vieneu-api.md) · [All operations](https://skmtc.dev/vieneu/apis/vieneu-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/vieneu/vieneu-api/revisions/11c8166a6c4d/schema)
