Text-to-Speech
Stream PCM16 24 kHz audio over socket.io.
Text-to-Speech (TTS) over Socket.IO.
This API accepts a JSON tts request and streams binary audio chunks via tts_audio.
Quickstart
- Connect with the
API_URLandAPI_KEYprovisioned for the environment;API_PATHis optional and defaults to/socket.io. - Request
tts_voice_list; an empty array is a valid result and must not be replaced with a guessed ID. - Emit
ttswith a fresh UUID,textcontaining at least one Unicode letter or number after trimming, and explicitmodel: "nebula". - Choose either one returned
voice_idor onevoice_referencesitem; do not send both. - Listen for
tts_audio, append bytes 17..end from every matching frame, including the final frame, and finish when byte 16 bit 0 is set.
Supported Features
- Streaming synthesis:
tts→tts_audio(binary chunks) - Voice profile discovery:
tts_voice_list→tts_voice_list_result(seven multilingual profiles) - Voice adaptation:
voice_references(base64 audio + transcript)
Input Validation
After trimming, top-level request text must contain at least one Unicode
letter or number. Whitespace-only input emits non-retryable
VALIDATION_REQUIRED_FIELD; punctuation-only input emits non-retryable
VALIDATION_INVALID_PARAM. The service counts Unicode code points, not
UTF-8 bytes or displayed grapheme clusters. Leading and trailing whitespace
is preserved and counts toward the limit.
Inclusive defaults are 500 code points for free accounts and 1,000 for
standard and enterprise accounts. Missing or unknown tiers use the free
limit. Deployments can independently override the limits with
TTS_MAX_INPUT_CHARACTERS_FREE, TTS_MAX_INPUT_CHARACTERS_STANDARD, and
TTS_MAX_INPUT_CHARACTERS_ENTERPRISE; consequently the schema does not
declare a fixed maxLength.
Text over the effective limit emits a non-retryable
CHARACTER_COUNT_EXCEEDED error carrying data with bound
tts_input_characters. Validation happens before synthesis and before the
per-key rate-limit bucket is charged.
text is plain UTF-8 text, not SSML. Markup is neither parsed nor
validated: angle brackets carry no meaning, count toward the character limit
like any other characters, and a tag's name may be spoken.
Content policy
When content-policy enforcement is enabled, rejected text emits
non-retryable TTS_INPUT_NOT_ALLOWED; change the text before trying again.
If the content-moderation authority cannot make a decision, synthesis fails
closed with retryable TTS_MODERATION_UNAVAILABLE. These outcomes are kept
distinct so an infrastructure failure is never reported as prohibited
content. Both are emitted before synthesis begins.
Voice-reference limits
voice_references accepts exactly one entry and every bound on it is checked
before the model lookup, the concurrency lease and the rate-limit charge, so
a rejected request consumes no quota and no slot:
- more than one entry emits non-retryable
VOICE_REFERENCE_COUNT_EXCEEDED(boundtts_voice_reference_count); - an explicit empty array emits non-retryable
VALIDATION_INVALID_PARAM- omit the field, or sendnull, to usevoice_idor the default voice; - supplying
voice_idtogether withvoice_referencesemits non-retryableVALIDATION_INVALID_PARAM; - a reference transcript over its own limit (default 500 code points,
independent of the
textbudget) emitsCHARACTER_COUNT_EXCEEDEDwithboundtts_voice_reference_text_characters; - decoded reference audio over the byte ceiling (default 2 MiB) emits
PAYLOAD_TOO_LARGEwithboundtts_voice_reference_bytes, checked from the base64 length before the payload is decoded; - decoded reference audio longer than the duration ceiling (default 15
seconds, matching the deployed model's own reference limit) emits
AUDIO_DURATION_EXCEEDEDwithboundtts_voice_reference_duration; - base64 that is not strictly canonical, or audio that is not mono PCM16
RIFF/WAVE, emits
VALIDATION_INVALID_FORMAT.
Deployments may lower the reference ceilings with
TTS_MAX_VOICE_REFERENCE_DECODED_BYTES,
TTS_MAX_VOICE_REFERENCE_DURATION_SEC and
TTS_MAX_VOICE_REFERENCE_TEXT_CHARACTERS but can never raise them above the
published defaults.
Lifecycle and cancellation
The Socket.IO connection owns its active synthesis RPCs; clients do not send
an owner field. Disconnecting cancels only that connection's requests and
suppresses later audio/error emits without closing inference connections
shared with other requests. Synthesis has a non-resetting 25-second overall deadline and a
60-second inactivity watchdog. If the overall deadline wins before the final
frame, the server emits retryable TTS_DEADLINE_EXCEEDED; audio already sent
remains partial and must be discarded or handled as incomplete.
TTS Audio Output Format
Audio bytes in tts_audio (bytes 17..end) are always:
- PCM16 little endian
- 24 kHz
- mono
- raw waveform (no WAV header)
Voice Selection
voice_id (if used) is a UUID returned by tts_voice_list_result.
If neither voice selector is sent, selection is deployment/model-defined and
no voice is guaranteed. Released SDK 0.18.0 applies stricter client-side
validation and requires exactly one selector.
The voice list contains only the seven configured multilingual profiles.
Every item carries profile metadata with one speaker context and an
open-ended list of supported language tags. Physical variant IDs are
internal and are rejected when supplied as voice_id.
For the current Arabic/English profiles, the Arabic variant is selected when
text contains any Unicode Arabic-script letter. Otherwise the English
variant is selected. Numbers, punctuation, emoji, whitespace, and letters
from non-Arabic scripts do not select Arabic.
A supplied voice_id is resolved before any quota deduction, rate-limit
charge or inference, and its failures are typed (SAU-2258): a voice_id that
is not a valid UUID emits non-retryable VALIDATION_INVALID_UUID; a
well-formed voice_id that does not identify an available voice emits
non-retryable TTS_VOICE_NOT_FOUND; a resolved voice whose stored data is
incomplete or corrupt emits non-retryable TTS_VOICE_RESOLUTION_FAILED; and a
positively classified transient database/storage outage during resolution
emits retryable SERVER_DEPENDENCY_FAILURE. Because resolution precedes any
charge, retrying the retryable case is safe.
Connection
Host: wss://api.voice.humain.com
Use the API_URL and API_KEY provisioned for the target environment.
API_PATH is optional and defaults to /socket.io, the single path that
serves every subsystem; set it only to override for a self-hosted or
proxied deployment. This is Socket.IO over WebSocket, not raw WebSocket.
Transport: WebSocket only — set transports: ["websocket"]. Polling is not supported.
Example (JavaScript):
const socket = io(process.env.API_URL, {
path: process.env.API_PATH ?? "/socket.io",
transports: ["websocket"],
extraHeaders: { "x-api-key": process.env.API_KEY, "Origin": process.env.API_URL }
});Example (Python — python-socketio):
async def main() -> None:
await sio.connect(
os.environ["API_URL"],
headers={"x-api-key": os.environ["API_KEY"], "Origin": os.environ["API_URL"]},
socketio_path=os.environ.get("API_PATH", "/socket.io"),
transports=["websocket"],
)
asyncio.run(main())Authentication: x-api-key (header httpApiKey).
Events
| Event | Direction | Description |
|---|---|---|
tts | Client → Server | Client sends JSON TTS request. |
tts_audio | Server → Client | Server streams TTS audio as binary chunks. |
tts_voice_list | Client → Server | Client requests available TTS voices. |
tts_voice_list_result | Server → Client | Server returns available TTS voices. |
error | Server → Client | Server emits error messages. |
Messages
TTS request
tts · Client → Server
Content type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | Unique identifier for this TTS request (echoed in responses). |
text | string | Yes | Text to synthesize. It must contain at least one Unicode letter or number after trimming whitespace: whitespace-only text is rejected with VALIDATION_REQUIRED_FIELD, and text with no letter or number (for example punctuation-only input such as -, ... or ؟) is rejected with VALIDATION_INVALID_PARAM. Both are emitted as a non-retryable error event carrying the request id, and synthesis is not attempted. Length is counted in Unicode code points, not UTF-8 bytes or displayed grapheme clusters; leading and trailing whitespace is preserved and counts. Inclusive defaults are 500 code points for free accounts and 1,000 for standard and enterprise accounts. Missing or unknown tiers use 500. Deployments can override each tier independently, so no fixed maxLength is stated. Exceeding the limit emits non-retryable CHARACTER_COUNT_EXCEEDED with data.bound tts_input_characters and unit characters. This field is plain UTF-8 text, not SSML; markup is neither parsed nor stripped and counts toward the limit. |
model | string | No | TTS model key. If omitted, the service uses its configured default; the code fallback is nebula. Send nebula explicitly for the published path instead of depending on deployment configuration. |
voice_id | string (uuid) | No | Optional profile UUID returned by tts_voice_list_result. The Arabic variant is selected when text contains an Arabic-script letter; otherwise the English variant is selected. Physical variant IDs are internal and are rejected. voice_id and voice_references are mutually exclusive: supplying both emits a non-retryable VALIDATION_INVALID_PARAM error, and an explicit empty voice_references array counts as supplying it. voice_references: null does NOT count, so voice_id with null is valid. If neither is sent, voice selection is deployment/model-defined. |
voice_references | TTSVoiceReference[] | null | No | Optional single voice reference used for voice adaptation. The current public route consumes only the first item, and the maxItems: 1 bound is enforced at runtime: supplying more emits a non-retryable VOICE_REFERENCE_COUNT_EXCEEDED error with data.bound tts_voice_reference_count. Omitting the field, or sending null, both mean "no reference"; null is accepted for client compatibility because many clients serialize an unset optional field that way, and it is declared in the type union rather than merely tolerated. An explicit empty array is a well-formed array that violates minItems: 1 and so emits VALIDATION_INVALID_PARAM. voice_references and voice_id are mutually exclusive, but null is not a second selector: voice_id together with voice_references: null is valid and uses voice_id. |
TTSVoiceReference
| Field | Type | Required | Description |
|---|---|---|---|
audio | string | Yes | Standard-base64 RIFF/WAVE audio containing non-empty mono PCM16 data. The base64 must be strictly canonical: line breaks, spaces, the URL-safe alphabet and non-zero padding bits are all rejected with VALIDATION_INVALID_FORMAT, as is anything that is not mono PCM16 RIFF/WAVE. Decoded size must not exceed the deployment's byte ceiling (default 2 MiB) - exceeding it emits PAYLOAD_TOO_LARGE with data.bound tts_voice_reference_bytes. Decoded duration must not exceed the duration ceiling (default 15 seconds, matching the deployed model's own reference limit) - exceeding it emits AUDIO_DURATION_EXCEEDED with data.bound tts_voice_reference_duration. Duration is computed from the file's own declared sample rate. Both bounds are inclusive. |
text | string | Yes | Transcript of the reference audio, counted in Unicode code points. It must contain at least one Unicode letter or number. Missing or whitespace-only text emits VALIDATION_REQUIRED_FIELD; text with no letter or number emits VALIDATION_INVALID_PARAM; exceeding the ceiling emits CHARACTER_COUNT_EXCEEDED with data.bound tts_voice_reference_text_characters. This ceiling is independent of the per-tier text limit and does not consume it. |
Examples
Basic TTS request
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"text": "Hello from HUMAIN Voice",
"model": "nebula"
}TTS request with a specific voice profile
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"text": "Hello from HUMAIN Voice",
"model": "nebula",
"voice_id": "19965876-8cd6-4b8c-9af4-35cbec69ff1d"
}TTS request using voice references
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"text": "Hello",
"model": "nebula",
"voice_references": [
{
"audio": "UklGRiYAAABXQVZFZm10IBAAAAABAAEAwF0AAIC7AAACABAAZGF0YQIAAAAAAA==",
"text": "Reference text"
}
]
}TTS audio stream
tts_audio · Server → Client
Raw binary audio stream chunks for TTS.
Content type: application/octet-stream
Raw unencoded binary buffer sent as a Socket.IO binary event (tts_audio).
Client should:
- Read request
idfrom bytes 0..15. - Check
end_of_streamin byte 16 (bit 0). - Append bytes 17..end to an output buffer until
end_of_stream=1.
Audio bytes format (bytes 17..end):
- PCM16 little endian @ 24 kHz, mono (raw waveform; no WAV header).
Byte layout (offsets in bytes):
- 0..15: id (UUID, 16 bytes)
- 16: header (uint8)
- bit 0: end_of_stream (final chunk)
- bits 1-7: reserved (0)
- 17..end: audio bytes
Example packet strings are illustrative notation. Spaces and
<audio-bytes> are not transmitted.
Examples
Non-final audio chunk
12ab34cd56ef789012ab34cd56ef7890 00 <audio-bytes>Final audio chunk
12ab34cd56ef789012ab34cd56ef7890 01 <audio-bytes>TTS voice list request
tts_voice_list · Client → Server
Content type: application/json
| Field | Type | Required | Description |
|---|
Examples
Request all available voices
{}TTS voice list result
tts_voice_list_result · Server → Client
Content type: application/json
Type: TTSVoiceListResult
List of available TTS voices.
Examples
List of available voice profiles
[
{
"id": "af52a907-1086-46f7-8f5d-72317875d7bd",
"label": "mul_youssef",
"profile": {
"speaker": {
"gender": "male",
"dialect": "arz"
},
"languages": [
"ar",
"en"
]
}
}
]No voices returned
[]Error message
error · Server → Client
Content type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | No | TTS request UUID (present when parseable from the payload) |
code | string | Yes | Machine-readable error code |
message | string | Yes | Human-readable error text |
retryable | boolean | Yes | Whether the client should retry |
timestamp | string (date-time) | Yes | UTC RFC 3339 timestamp created when the error is emitted. |
retry_after_seconds | integer | No | Seconds to wait before retrying. Present only on a retryable limit rejection. This transport has no Retry-After header, so the hint travels in the frame. |
data | object | No | Present only on limit rejections. Names the bound that was exceeded, its configured value and the observed value. |
Examples
Invalid JSON payload
{
"code": "VALIDATION_INVALID_FORMAT",
"message": "invalid json body provided",
"retryable": false,
"timestamp": "2026-01-15T10:30:00Z"
}The account already has as many concurrent TTS syntheses in flight as its plan allows, counted across every server instance. The limit is per BILLABLE ACCOUNT, so several API keys belonging to one account share one allowance. Retryable: it usually clears within seconds. The connection is NOT closed - other operations already admitted on this socket keep running.
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "CONCURRENCY_LIMIT_EXCEEDED",
"message": "too many concurrent operations for this account",
"retryable": true,
"timestamp": "2026-01-15T10:30:00Z",
"retry_after_seconds": 5,
"data": {
"limit": 4,
"observed": 4,
"unit": "operations",
"bound": "account_concurrency_tts"
}
}Text is empty or contains only Unicode whitespace
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "VALIDATION_REQUIRED_FIELD",
"message": "TTS input must contain non-whitespace text",
"retryable": false,
"timestamp": "2026-01-15T10:30:00Z"
}Text has no letter or number (punctuation-only)
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "VALIDATION_INVALID_PARAM",
"message": "TTS input must contain at least one letter or number",
"retryable": false,
"timestamp": "2026-01-15T10:30:00Z"
}Text rejected by the TTS content policy
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "TTS_INPUT_NOT_ALLOWED",
"message": "TTS input is not allowed",
"retryable": false,
"timestamp": "2026-01-15T10:30:00Z"
}Content-moderation authority temporarily unavailable
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "TTS_MODERATION_UNAVAILABLE",
"message": "TTS moderation is unavailable",
"retryable": true,
"timestamp": "2026-01-15T10:30:00Z"
}Text exceeds the default free-tier runtime limit
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "CHARACTER_COUNT_EXCEEDED",
"message": "TTS input contains 501 characters; limit is 500",
"retryable": false,
"timestamp": "2026-01-15T10:30:00Z",
"data": {
"limit": 500,
"observed": 501,
"unit": "characters",
"bound": "tts_input_characters"
}
}More than the published maxItems of 1
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "VOICE_REFERENCE_COUNT_EXCEEDED",
"message": "voice_references contains 2 references; limit is 1",
"retryable": false,
"timestamp": "2026-01-15T10:30:00Z",
"data": {
"limit": 1,
"observed": 2,
"unit": "references",
"bound": "tts_voice_reference_count"
}
}voice_id and voice_references were both supplied
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "VALIDATION_INVALID_PARAM",
"message": "voice_id and voice_references are mutually exclusive",
"retryable": false,
"timestamp": "2026-01-15T10:30:00Z"
}Decoded reference audio exceeds the per-reference byte ceiling
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "PAYLOAD_TOO_LARGE",
"message": "voice_references[0].audio decodes to 3145728 bytes; limit is 2097152",
"retryable": false,
"timestamp": "2026-01-15T10:30:00Z",
"data": {
"limit": 2097152,
"observed": 3145728,
"unit": "bytes",
"bound": "tts_voice_reference_bytes"
}
}Reference clip longer than the deployed model's reference limit
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "AUDIO_DURATION_EXCEEDED",
"message": "voice_references[0].audio is 16 seconds long; limit is 15",
"retryable": false,
"timestamp": "2026-01-15T10:30:00Z",
"data": {
"limit": 15,
"observed": 16,
"unit": "seconds",
"bound": "tts_voice_reference_duration"
}
}Reference transcript exceeds its own independent limit
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "CHARACTER_COUNT_EXCEEDED",
"message": "voice_references[0].text contains 501 characters; limit is 500",
"retryable": false,
"timestamp": "2026-01-15T10:30:00Z",
"data": {
"limit": 500,
"observed": 501,
"unit": "characters",
"bound": "tts_voice_reference_text_characters"
}
}Synthesis did not complete within the overall deadline
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "TTS_DEADLINE_EXCEEDED",
"message": "TTS synthesis deadline exceeded",
"retryable": true,
"timestamp": "2026-01-15T10:30:00Z"
}voice_id is present but not a valid UUID (SAU-2258)
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "VALIDATION_INVALID_UUID",
"message": "voice_id must be a valid UUID",
"retryable": false,
"timestamp": "2026-01-15T10:30:00Z"
}voice_id is a valid UUID but does not identify an available voice (SAU-2258)
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "TTS_VOICE_NOT_FOUND",
"message": "voice_id does not identify an available voice",
"retryable": false,
"timestamp": "2026-01-15T10:30:00Z"
}Resolved voice has incomplete/corrupt stored data; non-retryable (SAU-2258)
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "TTS_VOICE_RESOLUTION_FAILED",
"message": "selected voice could not be resolved",
"retryable": false,
"timestamp": "2026-01-15T10:30:00Z"
}Transient database/storage outage while resolving voice_id; retryable (SAU-2258)
{
"id": "7f51f2c2-e7bc-41c8-a850-f848df2ddfc8",
"code": "SERVER_DEPENDENCY_FAILURE",
"message": "voice resolution is temporarily unavailable",
"retryable": true,
"timestamp": "2026-01-15T10:30:00Z"
}