Using the Realtime Text to Speech API
Send text incrementally over a WebSocket connection and receive MP3 audio from the Speech service.
A realtime text-to-speech session lets your application send additional text while receiving generated audio. Authenticate the connection, start synthesis, append text, and finish input. Continue receiving audio until the service reports completion.
Realtime text to speech is currently available through the API only.
For the Console workflow, see Using Realtime Text to Speech.
Endpoint and Prerequisites
Use a regional realtime endpoint where the feature is available. The WebSocket path is /ws/synthesize/stream. For commercial OCI regions, the endpoint pattern is:
wss://realtime.aiservice.<region>.oci.oraclecloud.com/ws/synthesize/streamReplace <region> with a supported region identifier. Use the endpoint domain for your realm.
You need permission to use Speech synthesis and list voices in the selected compartment. See About Speech Policies for information about Speech access policies. Select a voice compatible with your model and language using ListVoices.
The examples use placeholders for your compartment, voice, and authentication values. Send client messages as JSON text messages.
Authenticate the Connection
Send an authentication object as the first WebSocket text message. Opening the connection alone doesn't authenticate the session.
- Convert the WebSocket URL to its HTTPS signing equivalent by replacing
wsswithhttps. Preserve the exact host, port, path, and query string. - Use an OCI signer to sign a
GETrequest for that URL. See Request Signatures. - Copy every header produced by the signer into the authentication object's
headersmap. Includehostanduriif the signer hasn't added them. Seturito the full HTTPS signing URL. - Send the object with
authenticationTypeset toCREDENTIALS. Use the same compartment for authentication and the synthesis request.
{
"authenticationType": "CREDENTIALS",
"compartmentId": "<compartment-ocid>",
"headers": {
"authorization": "<authorization-header-from-OCI-signer>",
"date": "<date-header-from-OCI-signer>",
"host": "realtime.aiservice.<region>.oci.oraclecloud.com",
"uri": "https://realtime.aiservice.<region>.oci.oraclecloud.com/ws/synthesize/stream"
}
}
Include the signer's date or x-date header and any additional headers it produces. The endpoint doesn't support TOKEN as the value of authenticationType.
Wait for both the authenticated and connected events before starting synthesis. They can arrive in either order.
{
"event": "authenticated",
"sessionId": "<authentication-session-id>"
}
{
"event": "connected",
"sessionId": "<synthesis-session-id>"
}
The two session IDs can differ. Use the ID from connected to identify subsequent synthesis events.
Start Synthesis
Send one start action per connection. Include nonblank initial text, set isStreamEnabled to true, and select a compatible model and voice. This example uses Natural synthesis with US English and MP3 output:
{
"action": "start",
"request": {
"compartmentId": "<compartment-ocid>",
"text": "Welcome to the garden tour. ",
"isStreamEnabled": true,
"configuration": {
"modelFamily": "ORACLE",
"modelDetails": {
"modelName": "TTS_2_NATURAL",
"voiceId": "<compatible-voice-id>",
"languageCode": "en-US"
},
"speechSettings": {
"textType": "TEXT",
"outputFormat": "MP3",
"sampleRateInHz": 24000,
"speechMarkTypes": []
}
}
}
}
For Standard synthesis, set modelName to TTS_1_STANDARD, select a compatible voice, and omit languageCode from modelDetails.
Wait for session_started before appending text:
{
"event": "session_started",
"sessionId": "<synthesis-session-id>",
"outputFormat": "MP3",
"contentType": "audio/mpeg"
}
Append Text and Finish Input
After session_started, send additional text while continuing to receive audio:
{
"action": "append_text",
"text": "Our first stop is the fern collection. "
}
Preserve the spaces between segments and send each segment only once. Don't repeat text from the start request.
For typed or generated text, buffer characters through the latest whitespace boundary before sending them. Keep any trailing word fragment for the next message. When input is complete, send the remaining text, including a final word without trailing whitespace. Punctuation alone can occur inside a word or value, so it isn't a reliable boundary.
After all text has been sent, send end_input:
{
"action": "end_input"
}
Don't append text after this action. Continue processing audio until completed. The service can send an input_ended acknowledgement, but your client must not depend on receiving it to process audio.
Receive Audio and Check Completion
The service sends audio in JSON text events. Each chunk contains base64-encoded bytes from the same MP3 stream:
{
"event": "audio_chunk",
"sessionId": "<synthesis-session-id>",
"chunkIndex": 0,
"encoding": "base64",
"contentType": "audio/mpeg",
"chunk": "<base64-encoded-mp3-data>",
"audioDurationMs": 740.0
}
For each audio event:
- Check that
sessionIdmatches the ID fromconnected. - Check that
chunkIndexstarts at zero and increases by one for each chunk. - Check that
encodingisbase64andcontentTypeisaudio/mpeg. - Decode
chunkand verify that the decoded audio isn't empty. - Append the decoded bytes in order. To save the audio, write the combined bytes to an
.mp3file. - Check that the cumulative
audioDurationMsdoesn't decrease.
Keep receiving events after end_input. A successful session ends with completed, for example:
{
"event": "completed",
"sessionId": "<synthesis-session-id>",
"chunksProduced": 12,
"audioDurationMs": 3840.0
}
Before accepting the audio as complete, verify that you received at least one nonempty chunk, that chunksProduced matches your received chunk count, and that the final duration is positive and no less than the last chunk's duration. The values in these event examples are illustrative.
Treat error and fatal_error events as failures. Process these errors before checking the session ID because an error event might not include one. If the connection ends without a completion event, any audio received can be incomplete.
Cancel a Session
To stop an active session, send:
{
"action": "cancel"
}
The service can respond with cancelled. Don't send further text or control actions after cancellation. Open and authenticate a new connection for another synthesis session.
Input and Session Limits
- The combined text in
start.request.textand allappend_text.textmessages must not exceed 10,000 characters. The Console has a separate limit of 5,000 characters. - The initial text must be a nonblank JSON string. Appended text must also be a JSON string.
- Use a compatible model, language, voice, and sample rate. The sample rate must be a whole number from 8,000 through 48,000 Hz.
- Send one
startaction per WebSocket connection. Don't reuse a completed or cancelled session. - Send JSON text messages. Binary client messages aren't supported.
Troubleshooting
| Error | What to check |
|---|---|
authentication_failed | Check the signature, signed URL and host, current signing date, compartment, and permissions. Send the authentication object first. |
authentication_unavailable or upstream_connect_error | The service couldn't complete authentication or establish the synthesis connection. Retry transient failures with bounded exponential backoff. |
pending_input_limit_exceeded | Wait for both readiness events before sending start, then wait for session_started before appending text. |
binary_messages_not_supported | Send a JSON text message instead of a binary WebSocket message. |
Bad Payload | Check the JSON structure, field names, action sequence, and the model, voice, language, and output settings. |
| HTTP 503 | The endpoint or synthesis service is temporarily unavailable. Retry with bounded exponential backoff. |
Correct malformed requests or authorization failures before retrying. If audio stops unexpectedly, check for an error event and retain the connection's close reason for troubleshooting.