To integrate your agent into your website or mobile app, use this endpoint. You can send and receive audio in real time, which allows for seamless and natural conversations.
Step 1. Create a widget agent
In your Anunzi dashboard, create an agent of the type widget.
Go to Agents and select the agent you have just created. Copy the ID next to its name. This is the ID you will use as the assistant_id parameter.
Step 2. Request a session
Make a GET request to the following endpoint:
curl -X GET "https://widgets.anunzi.net/websocket/token/{assistant_id}" \
-H "Authorization: Bearer <your-anunzi-api-key>"
class Processor extends AudioWorkletProcessor {
constructor(options) {
super();
this.buffer = getAudioBuffer();
this.port.onmessage = (ev) => {
this.buffer.pushArray(new Int16Array(ev.data.buffer));
};
}
process(inputs, outputs, parameters) {
const output = outputs[0][0];
for (let i = 0; i < output.length; i++) {
let value = this.buffer.getSample();
if (value === undefined) {
break;
}
output[i] = value / 32768;
}
return true;
}
}
function getAudioBuffer() {
let samplePointer = 0;
/**
* @type {Array<Int16Array>}
*/
let arrays = [];
/**
* @type {Int16Array | undefined}
*/
let currentArray = undefined;
return {
getSample: () => {
if (currentArray === undefined || samplePointer >= currentArray.length) {
currentArray = arrays.shift();
samplePointer = 0;
}
if (currentArray === undefined) {
return undefined;
}
const sample = currentArray[samplePointer];
samplePointer++;
return sample;
},
pushArray: (array) => {
arrays.push(array);
},
};
}
registerProcessor("audio-out-worklet", Processor);
const MESSAGE_TYPE_STATUS_AGENT_READY = "status_agent_ready";
const MESSAGE_TYPE_STATUS_CLIENT_READY = "status_client_ready";
async function startSynthflow(
url: string,
playAudioChunk: (chunk: Int16Array) => void
): Promise<{
stop: () => void;
}> {
updateStatus("Connecting to server", "warning");
const websocket = new WebSocket(
url
);
websocket.binaryType = "arraybuffer";
websocket.onmessage = async (event) => {
// Handle audio
if (event.data instanceof ArrayBuffer) {
const arrayBuffer = event.data as ArrayBuffer;
const pcmSamples = new Int16Array(arrayBuffer);
playAudioChunk(pcmSamples);
} else {
const data = JSON.parse(event.data);
switch (data.type) {
case MESSAGE_TYPE_STATUS_AGENT_READY:
updateStatus("Connected to call", "success");
console.log("Received agent ready message");
break;
default:
console.log("Received unknown message from server", data);
break;
}
}
};
const recordingControls = await startRecording(
(audio) => {
if (websocket.readyState === WebSocket.OPEN) {
websocket.send(audio);
}
},
async () => {
sendWhenReady(websocket, JSON.stringify({ type: MESSAGE_TYPE_STATUS_CLIENT_READY }))
console.log("Scheduled send client ready message");
}
);
websocket.onclose = () => {
recordingControls.stop();
updateStatus("Disconnected from server", "error");
};
return {
stop: () => {
websocket.close();
},
};
}
function base64ToArrayBuffer(base64: string): ArrayBuffer {
const binaryString = window.atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
function sendWhenReady(websocket: WebSocket, message: string) {
if (websocket.readyState === WebSocket.CLOSED) {
console.log("WebSocket is closed, not sending message");
return;
} else if (websocket.readyState === WebSocket.OPEN) {
websocket.send(message);
} else {
setTimeout(() => sendWhenReady(websocket, message), 50);
}
}
async function makeCall() {
const sessionURL = ... // make a call to your server to get a session URL
const audioOutControls = await startAudioOut();
const synthflowControls = await startSynthflow(sessionURL, (audio) => {
audioOutControls.enqueueAudioChunk(audio);
});
// stop audio/call when needed
}