Guides · Live API
TypeScript
Connect over a plain WebSocket to send microphone audio through the .wave Live API and receive the model’s audio plus transcripts.
Install and connect
npm install ws
export DOTWAVE_API_KEY="wk_…"import WebSocket from 'ws';
const ws = new WebSocket('wss://api.dotwave.ai/v1/live/sessions', {
headers: { Authorization: `Bearer ${process.env.DOTWAVE_API_KEY}` },
});
ws.on('open', () => {
// session.start must be the first event, or the socket refuses it.
ws.send(JSON.stringify({
type: 'session.start',
session: {
model: 'nemotron-voicechat',
audio: {
input: {
format: { type: 'audio/pcm', rate: 24000 },
},
output: {
format: { type: 'audio/pcm', rate: 24000 },
encoding: 'base64',
},
},
},
}));
});
ws.on(
'message',
raw => {
const event = JSON.parse(raw.toString());
switch (event.type) {
case 'session.input_transcript.delta':
process.stdout.write(event.delta);
break;
case 'session.output_transcript.delta':
process.stdout.write(event.delta);
break;
case 'session.output_audio.delta':
playPcm24k(Buffer.from(event.delta, 'base64'));
break;
}
},
);Microphone source
Configure your capture library for mono PCM16 and pass each chunk straight to this function. The capture library controls the device; this function controls the socket.
function sendPcmChunk(chunk: Buffer) {
ws.send(JSON.stringify({
type: 'session.input_audio.append',
audio: chunk.toString('base64'),
}));
}
// Existing microphone callback:
microphone.on('data', sendPcmChunk);Send a recorded turn
Raw PCM files have no timing of their own, so pace each chunk against an absolute clock. Live appends audio straight onto the session, so there is no buffer to commit when the turn ends.
import { open } from 'node:fs/promises';
import { setTimeout as delay } from 'node:timers/promises';
async function sendPcmFile(path: string) {
const file = await open(path, 'r');
const chunk = Buffer.alloc(3840);
const started = performance.now();
let sent = 0;
try {
while (true) {
const { bytesRead } = await file.read(chunk, 0, chunk.length, null);
if (!bytesRead) break;
sendPcmChunk(chunk.subarray(0, bytesRead));
sent += bytesRead;
const audioMs = sent / (24000 * 2) * 1000;
await delay(Math.max(0, started + audioMs - performance.now()));
}
} finally {
await file.close();
}
}Reconnects
Handle capacity errors before audio starts. If the socket closes unexpectedly, open a new session and continue with new audio; do not replay audio from the previous session.
Limits
Session length, voices and supported fields depend on the model: see its page in Models. A field the model does not support is refused, never silently ignored.