Zum Hauptinhalt springen

IVR JS Example (OpenAI Basic Call Transfer)

IVR JS Example - OpenAI Basic Call Transfer

This example demonstrates a basic IVR script that uses the power of OpenAI by integrating it with Vodia's Javascript IVR 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.

Setup:

  • Create a new project in OpenAI platform for your SIP connection to OpenAI.
  • Get the project id, and the related API key, to be used below.
  • Setup a webhook in this project - a URL that can reach your Vodia PBX - with the endpoint e.g.: https://YOUR-VODIA-TENANT.com/openai. The "/openai" is the default, but you can change it here and the PBX.
  • Create a new "OpenAI" trunk in Vodia PBX for connection to OpenAI. Make sure to enter the project id (taken above) as the username when creating the trunk.
  • Create a dialplan entry for use of that trunk with the Pattern as openai and replacement as empty.
  • Now you can use an IVR to use JS and the API key to connect (WS) to OpenAI for setting up instructions and receive responses as setup by the instruction like function calls for call routing etc.
  • One example is given below.

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.