Realtime HTTP
Choose complete-audio, live framed, or text-to-speech HTTP operations and handle their streams safely.
The /realtime/http/* operations are direct platform APIs. SDK 0.18.0
doesn't wrap them. Use them only from a trusted runtime that can protect
x-api-key, enforce deadlines, and implement the validated OpenAPI framing.
1. Choose the operation by input lifecycle
| Input and outcome | Choose | Request unit | Completion signal |
|---|---|---|---|
| A long or large complete meeting, podcast, or archive | Batch REST, not a realtime HTTP operation | One complete file, then job polling | Batch done, failed, or cleared |
| One complete latency-sensitive conversational unit | POST /realtime/http/stt | One multipart/form-data file | NDJSON STTResponse with is_final: true |
| Audio still arriving when you need live text | POST /realtime/http/stt-stream | One 18-byte-header-plus-PCM body per request | An observed is_speech_final marks a speech boundary; only an observed is_final completes the stream |
| Audio still arriving when you need a speaker timeline | POST /realtime/http/diarization-stream | One framed PCM body per serialized request | An observed record with is_final: true; an active provisional tail can remain |
| Text must become speech | SDK Socket.IO TTS for playable output; direct HTTP only for a protocol capture | One JSON request | SDK completion; direct HTTP has no generically detectable frame boundary |
Fast transcription streams result lines, but its input is still one complete file. It isn't a live microphone transport.
2. Prepare authentication, identifiers, and deadlines
- Obtain
API_KEYthrough the authentication flow, and use theAPI_URLrendered for this environment. - Send
x-api-keyon every request from a trusted backend. Browser or mobile code can't keep this credential secret. The key also needs the provisioned capability: realtime ASR for Fast/live ASR, diarization for the speaker stream, or TTS for synthesis. - Generate a valid UUID for
id. Fast STT carries it in the query, TTS carries it in JSON, and live frames carry its 16 raw bytes. Reuse one UUID only for the requests in the same live stream. - Set a connect timeout, a finite timeout for each request and read, and an overall operation deadline. Validate the HTTP status before parsing a success stream.
- Buffer NDJSON across transport reads and split only on newline. One network read can contain part of a line or several lines.
API_PATH applies to Socket.IO clients and isn't used by these HTTP routes.
3. Send one complete unit for fast transcription
Supply a UUID in id and one complete file in the multipart file field. Fast
applies only language and ASR model selection:
| Selector | Accepted values |
|---|---|
language or lang | en, ar, codeswitch, auto |
asr or model | Published model wire value |
A nonempty language takes precedence over lang; a nonempty asr takes
precedence over model. Omitted or auto language and an omitted model use
defaults configured for the environment. Use Batch when diarization, ITN, or
redaction is required.
export REQUEST_ID="7f51f2c2-e7bc-41c8-a850-f848df2ddfc8"
curl -N --fail-with-body --connect-timeout 10 --max-time 120 \
"${API_URL%/}/realtime/http/stt?id=$REQUEST_ID&language=codeswitch&asr=bayan_cs_ar_en" \
-H "x-api-key: $API_KEY" \
-F "file=@turn.wav"HTTP 200 returns application/x-ndjson. Each non-empty line is one complete
JSON object; for example:
{"id":"7f51f2c2-e7bc-41c8-a850-f848df2ddfc8","seq":0,"transcription":"hello wor","words":[{"start_time":0.0,"end_time":0.45,"word":"hello"}],"is_final":false}
{"id":"7f51f2c2-e7bc-41c8-a850-f848df2ddfc8","seq":0,"transcription":"hello world","words":[{"start_time":0.0,"end_time":0.45,"word":"hello"},{"start_time":0.46,"end_time":0.9,"word":"world"}],"is_final":true}Process records in observed arrival order and retain seq only for diagnostics;
the current public Fast contract does not define ordering or uniqueness for it.
Treat is_final: false as provisional and finish the request state only after
is_final: true. Don't treat raw HTTP read chunks as records or assume that
every provisional transcription is append-only.
After partial output has started, a later failure ends the partial HTTP 200
without appending an error JSON record. EOF, cancellation, or an application
deadline without is_final: true is incomplete and ambiguous; the operation
defines no replay contract, so do not blindly resubmit the audio.
4. Frame live ASR and diarization
Both live operations accept application/octet-stream. Every request body has
this layout:
Use one fresh nonzero UUID throughout. Set the start bit on the first request,
neither flag on intermediate requests, and the final bit on the last request;
set both for a one-chunk stream and keep reserved flag bits zero. Every request
must carry audio. A framed file such as frame.bin can be sent with
--data-binary; it isn't an audio file by itself because it includes the
18-byte control header.
Build a valid frame
These tested builders default to a one-chunk stream, so both boundary flags are
set. For multiple chunks, reuse STREAM_ID, set IS_FINAL=0 on the first
chunk, set both flags to 0 on intermediate chunks, and set only IS_FINAL=1
on the last chunk.
import { randomUUID } from 'node:crypto';
import { readFile, writeFile } from 'node:fs/promises';
const languageBytes = {
ar: 0,
en: 1,
codeswitch: 2,
auto: 255,
} as const;
function uuidBytes(id: string): Uint8Array {
const hex = id.replaceAll('-', '');
if (!/^[0-9a-f]{32}$/i.test(hex) || /^0{32}$/.test(hex)) {
throw new Error('STREAM_ID must be a nonzero UUID');
}
return Uint8Array.from(hex.match(/.{2}/g)!, (byte) => Number.parseInt(byte, 16));
}
function frame(
id: string,
pcm16le: Uint8Array,
options: { language: keyof typeof languageBytes; isStart: boolean; isFinal: boolean },
): Uint8Array {
if (pcm16le.byteLength === 0 || pcm16le.byteLength % 2 !== 0) {
throw new Error('PCM16 payload must be nonempty and contain an even number of bytes');
}
const output = new Uint8Array(18 + pcm16le.byteLength);
output.set(uuidBytes(id), 0);
output[16] = (options.isStart ? 1 : 0) | (options.isFinal ? 2 : 0);
output[17] = languageBytes[options.language];
output.set(pcm16le, 18);
return output;
}
async function main(): Promise<void> {
const inputPath = process.argv[2] ?? 'chunk.pcm';
const outputPath = process.argv[3] ?? 'frame.bin';
const streamId = process.env.STREAM_ID ?? randomUUID();
const pcm = await readFile(inputPath);
// Defaults build a valid one-chunk stream. For a longer stream, reuse
// STREAM_ID and set only the boundary flags for each arriving PCM chunk.
const body = frame(streamId, pcm, {
language: 'codeswitch',
isStart: process.env.IS_START !== '0',
isFinal: process.env.IS_FINAL !== '0',
});
await writeFile(outputPath, body);
console.info({ streamId, bytes: body.byteLength, outputPath });
}
void main().catch((error: unknown) => {
console.error(error);
process.exitCode = 1;
});
Live ASR
curl -N --fail-with-body --connect-timeout 10 --max-time 30 \
-X POST "${API_URL%/}/realtime/http/stt-stream" \
-H "x-api-key: $API_KEY" \
-H "content-type: application/octet-stream" \
--data-binary @frame.binSend one POST per framed audio chunk. Each HTTP 200 contains zero or more
NDJSON records with id, seq, transcription, words, is_speech_final,
and is_final. A later failure ends a partial 200 without appending an error
record. Buffer across reads and parse complete lines. is_speech_final marks a detected
speech-segment boundary; only an observed is_final: true completes the whole
stream. The request final bit and an ending response, including an empty 200,
do not. Treat seq as opaque and reconcile provisional text by id and
observed arrival order as described in the
realtime lifecycle guide.
The public contract does not define whether chunk POSTs should overlap or be serialized. Use only the coordination pattern provisioned for your environment; do not infer routing safety from ordinary HTTP concurrency.
A normal non-final response window ends after two seconds and preserves the session. Aborting a POST or timing out the final response cancels it. The session expires after 60 seconds without accepted client audio or an inference response.
POST /realtime/http/realtime-asr is a compatibility path. New clients should
use the canonical POST /realtime/http/stt-stream operation.
Live diarization
curl -N --fail-with-body --connect-timeout 10 --max-time 30 \
-X POST "$API_URL/realtime/http/diarization-stream" \
-H "x-api-key: $API_KEY" \
-H "content-type: application/octet-stream" \
--data-binary @frame.binThis operation has no query parameters, multipart form, or client model
selector. The language byte must be 0, 1, 2, or 255, but the service
discards it after validation. Mark the last real audio frame final because an
empty terminator is invalid. A stream that never received a start frame returns
HTTP 400 with VALIDATION_REQUIRED_FIELD.
Keep at most one POST in flight for each stream UUID. Capture audio concurrently into a bounded queue, but use one sender to drain it and close each response before sending the next frame. Concurrent same-UUID requests can overwrite response ownership; distinct UUID streams can run concurrently.
Each HTTP 200 contains zero or more NDJSON records with id,
final_segments, active_segments, and is_final. A later failure ends a
partial 200 without appending an error record. Accumulate unseen final_segments because each array
is a per-record delta. Replace the prior active_segments snapshot, then sort
the reconciled final-plus-active timeline by start_time. Speaker labels are
relative to one stream, not identities, and segment times are relative to its
start.
Only an observed is_final: true completes the stream. A final request bit,
empty 200, EOF, or response deadline does not. A final record can retain a
nonempty active tail; preserve it as provisional instead of silently marking it
final. A normal non-final response window ends after two seconds and preserves
the session. Aborting a POST or timing out the final response cancels it, and
the session expires after 60 seconds without client or inference activity.
There is no chunk replay, resume, or idempotency contract. After an ambiguous
failure, close every response, mark the timeline incomplete, and recover with a
fresh UUID rather than replaying an old chunk.
5. Request HTTP TTS with its output contract in mind
The JSON body requires a fresh id and text containing at least one Unicode
letter or number after trimming. Optional fields are model, voice_id, and
voice_references. For a predictable voice, send one UUID voice_id or one
reference whose audio is standard-base64 RIFF/WAVE with non-empty mono PCM16
data and whose text is its transcript. The selectors are mutually exclusive.
Obtain voice_id through SDK listVoices() or list_voices(); there is no HTTP
voice-list operation. Set model to nebula explicitly instead of relying on
the deployment-configured default, which falls back to nebula.
curl --fail-with-body --connect-timeout 10 --max-time 120 \
-X POST "$API_URL/realtime/http/tts" \
-H "x-api-key: $API_KEY" \
-H "content-type: application/json" \
--data '{"id":"7f51f2c2-e7bc-41c8-a850-f848df2ddfc8","text":"Hello from HUMAIN Voice","model":"nebula"}' \
--output tts-frames.binHTTP 200 returns a continuous application/octet-stream body:
The service appends a final frame even when that frame has no additional PCM.
The contract defines no frame-length field or delimiter. HTTP reader chunks are
transport chunks and aren't guaranteed to match service-frame boundaries, so a
generic client can't safely remove 17 bytes from every read. The example's
tts-frames.bin is a protocol capture, not a playable PCM or WAV file.
If synthesis fails after bytes were committed, the partial binary 200 stream
simply ends: no structured error JSON is appended. A missing boundary-aware final
flag, premature EOF, or deadline therefore leaves an incomplete capture with no
in-band explanation, and only a failure occurring before output was committed can
be reported as structured JSON. Aborting the HTTP request cancels its in-flight
synthesis. Direct HTTP model, capacity and inference failures are reported as retryable
500 TTS_SYNTHESIS_FAILED; a gateway can independently return 429. A
content-policy rejection is non-retryable 400 TTS_INPUT_NOT_ALLOWED; change
the text instead of resending it. If the moderation authority cannot decide,
synthesis fails closed with retryable 503 TTS_MODERATION_UNAVAILABLE; do not
misreport that infrastructure failure as prohibited content. A caller-supplied
voice_id no longer collapses into TTS_SYNTHESIS_FAILED (SAU-2258): an
unparseable voice_id is 400 VALIDATION_INVALID_UUID and a well-formed one that
does not identify an available voice is 400 TTS_VOICE_NOT_FOUND (both
non-retryable); a resolved voice whose stored data is incomplete or corrupt is
500 TTS_VOICE_RESOLUTION_FAILED (non-retryable); and a transient
database/storage outage during voice resolution is 503 SERVER_DEPENDENCY_FAILURE
(retryable).
Text and voice-reference problems no longer collapse that way. Over-limit text,
an over-limit reference transcript, more than one reference, and a reference clip
longer than the deployment's configured reference ceiling are non-retryable
422; a reference
whose decoded size is over the ceiling is 413; and a malformed reference,
supplying both voice selectors, or an explicit empty voice_references array are
non-retryable 400. All are checked before any model lookup, admission or
charge, and each limit rejection carries a data object naming the bound, its
configured value and the observed value. See
Errors and rate limits.
Until your direct client has an unambiguous frame-boundary mechanism, use SDK Socket.IO TTS and the TTS-to-WAV recipe for playable output. Don't assume the Socket.IO 24 kHz output format for this 16 kHz HTTP operation.
6. Bound failures and clean up every stream
- Validate the HTTP status before selecting the NDJSON or binary success
parser. A deployment gateway can return
429; current backends can instead collapse capacity failures into retryable500withASR_TRANSCRIPTION_FAILED,DIARIZATION_FAILED, orTTS_SYNTHESIS_FAILED. Treat each layer's actual status and body as evidence, and do not infer a numeric quota or reset window. - For
ErrorResponse, branch oncodeandretryable, not the wording oferror,detail, ormessage. - On a live-stream failure or deadline, stop sending frames, abort the request, close its response reader, and start recovery with a fresh UUID. Session resume after interruption isn't documented.
- A timeout after sending the complete multipart file is ambiguous. The contract doesn't define idempotent replay, so don't repeat it blindly.
- If a boundary-aware TTS client ends without a final frame, keep partial bytes separate from complete output and close the response. Don't infer completion from connection close alone.
Apply bounded retry only when the structured error permits it, the operation is safe under your application policy, and the overall deadline remains. See Errors and Rate Limits.
7. Continue with the contract and production checks
Start with the smallest representative request for the chosen operation and verify its documented final signal. Keep the generated OpenAPI reference beside your implementation for exact parameters, schemas, and errors, then exercise deadlines, malformed frames, disconnects, and cleanup before launch.
Realtime HTTP reference
Inspect the validated operations, parameters, binary schemas, status codes, and errors.
Long-form Batch REST
Route meetings, podcasts, archives, and other long complete recordings to a polled batch job.
Realtime lifecycle
Reconcile provisional results and recover conservatively from interruptions.