Skip to main content

Voice Agents JS Example (OpenAI Basic Call Transfer)

Voice Agents JS Example - OpenAI Basic Call Transfer

This example demonstrates a basic Voice Agents script that uses the power of OpenAI by integrating it with Vodia's Javascript Voice Agents capabilities. The audio (RTP) is setup through a SIP call to OpenAI. And the call control, OpenAI responses as function calls etc. on which to take action are done through the websocket connection to the same call by using the unique callid sent by OpenAI through the webhook.

OpenAI Setup:

Please refer to the documentation here for OpenAI setup, including configuring project, the webhook, API key and then setting up the trunk and dialplan on Vodia.

Scenario:

  • Send the SIP call to OpenAI SIP interface (to the project_id created in OpenAI), using Vodia's trunk and dialplan.
  • OpenAI will send a webhook (that is set in the project) back with a unique callid.
  • Accept the call through the API using that callid.
  • While that establishes the audio call through RTP, now setup a WebSocket connection to that call using that same callid.
  • Using the websocket, you can set the instructions to OpenAI, in this case a map of words to numbers, the initial greeting, and receive reponses as strings etc.
  • Parse OpenAI's response to determine the appropriate call routing.
note

The model specified in the script is gpt-realtime. You must verify its current availability under your OpenAI plan. Additionally, check for potential rate limit/cost impacts and identify a suitable alternative model should the version be inaccessible

//
// OpenAI integration
//
// (C) Vodia Networks 2025
//
// This file is property of Vodia Networks Inc. All rights reserved.
// For more information mail Vodia Networks Inc., info@vodia.com.
//
'use strict'

var secret = "sk-proj-KEY"

var texts = {
initial: {
en: "Hi, I am your Vodia AI assistant. How may I help you today?"
}
}

function text(name) {
var prompt = texts[name]
if (call.lang in prompt) return prompt[call.lang]
return prompt["en"]
}

var timer = setTimeout(function() {
call.transfer('700')
}, 30000)

call.http(onhttp)

function onhttp (args) {
console.log('Open AI Ringing ... ')
console.log(JSON.stringify(args))
const body = JSON.parse(args.body)
if (body.type == 'realtime.call.incoming') {
const callid = body.data.call_id
system.http({
method: 'POST',
url: 'https://api.openai.com/v1/realtime/calls/' + callid + '/accept',
header: [
{ name: 'Authorization', value: 'Bearer ' + secret, secret: true },
{ name: 'Content-Type', value: 'application/json' }
],
body: JSON.stringify({
type: "realtime",
model: "gpt-realtime",
instructions: "You are a helpful assistant."
}),
callback: function(code, response, headers) {
connected(code, response, headers, callid)
}
})
}
}

function connected(code, response, headers, callid) {
var ws = new Websocket("wss://api.openai.com/v1/realtime?call_id=" + callid)

ws.header([
{ name: "Authorization", value: "Bearer " + secret, secret: true },
{ name: "User-Agent", value: "Vodia-PBX/Version" }
])

ws.on('open', function() {
console.log("Websocket opened")
const update = {
"type": "session.update",
"session": {
"type": "realtime",
"instructions": ". Here is a map of words to numbers: { Sales: 4001, Marketing: 4002, Support: 4003 }. Whenever someone says call, transfer to, would like to talk to, or get someone online, you MUST call the function 'transfer_call'. The function argument 'destination' must be the resolved number from the map. If the user provides a number directly, use it as-is. If the destination is not found in the map, use 700. Do not respond with normal text for these intents.",
"tools": [
{
"type": "function",
"name": "transfer_call",
"description": "Transfers the active SIP call to a destination number",
"parameters": {
"type": "object",
"properties": {
"destination": {
"type": "string",
"description": "Phone number to transfer to"
}
},
"required": ["destination"]
}
}
],
"tool_choice": "auto"
}
}
ws.send(JSON.stringify(update))

const greeting = {
"type": "response.create",
"response": {
"instructions": "Greet with: " + text('initial')
}
}
ws.send(JSON.stringify(greeting))
})
ws.on('close', function() { console.log("Websocket closed") })

ws.on('message', function(message) {
//call.log('Websocket message:')
//call.log(message)
const evt = JSON.parse(message)
if (
evt.type === "response.output_item.done" &&
evt.item.type === "function_call" &&
evt.item.name === "transfer_call"
) {
const args = JSON.parse(evt.item.arguments)
const destination = args.destination
console.log('Transfer to destination:')
console.log(destination)
call.transfer(destination)
}
})

ws.connect()
}

call.dial('openai')

For more information on Vodia's JavaScript capabilities, refer to: Vodia Backend JavaScript DocumentationTo run code, enable code execution and file creation in Settings > Capabilities.