Example: Answering an incoming call

View as Markdown

For the complete documentation index, see llms.txt.

This example answers an inbound Voximplant call and bridges audio to OpenAI Realtime for speech-to-speech conversations. Select a model version to keep the session configuration and complete scenario synchronized. gpt-realtime-2.1 is the default for new applications.

Jump to the Full VoxEngine scenarios.

Prerequisites

Session setup

gpt-realtime-2 and above introduced some breaking changes to the gpt-realtime-1.x session configuration object. Make sure to select the appropriate version in the examples below.

The example configures the OpenAI Realtime session with a system prompt, enables semantic VAD, and uses low reasoning effort.

GPT Realtime 2.1 session setup
1voiceAIClient.sessionUpdate({
2 session: {
3 type: "realtime",
4 model: "gpt-realtime-2.1",
5 instructions: SYSTEM_PROMPT,
6 reasoning: { effort: "low" },
7 output_modalities: ["audio"],
8 audio: {
9 input: {
10 turn_detection: {
11 type: "semantic_vad",
12 eagerness: "auto",
13 create_response: true,
14 interrupt_response: true,
15 },
16 },
17 output: {
18 voice: "alloy",
19 },
20 },
21 },
22});

Connect call audio

Once the session is ready, bridge audio both ways between the call and OpenAI:

Connect call audio
1VoxEngine.sendMediaBetween(call, voiceAIClient);

Barge-in

The scenario clears buffered model audio whenever OpenAI detects caller speech:

Barge-in
1voiceAIClient.addEventListener(OpenAI.RealtimeAPIEvents.InputAudioBufferSpeechStarted, () => {
2 voiceAIClient.clearMediaBuffer();
3});

Full VoxEngine scenarios

voxeengine-openai-answer-incoming-call.js
1/**
2 * Voximplant + OpenAI Realtime API connector demo
3 * Scenario: answer an incoming call and bridge it to OpenAI Realtime 2.1.
4 */
5
6require(Modules.OpenAI);
7
8const SYSTEM_PROMPT = `
9You are Voxi, a helpful voice assistant for phone callers.
10Keep responses short and telephony-friendly (usually 1-2 sentences).
11`;
12
13const SESSION_CONFIG = {
14 session: {
15 type: "realtime",
16 model: "gpt-realtime-2.1",
17 instructions: SYSTEM_PROMPT,
18 reasoning: {effort: "low"},
19 output_modalities: ["audio"],
20 audio: {
21 input: {
22 turn_detection: {
23 type: "semantic_vad",
24 eagerness: "auto",
25 create_response: true,
26 interrupt_response: true,
27 },
28 },
29 output: {
30 voice: "alloy",
31 },
32 },
33 },
34};
35
36VoxEngine.addEventListener(AppEvents.CallAlerting, async ({call}) => {
37 let voiceAIClient;
38 const eventPayload = (event) => event?.data?.payload || event?.data || {};
39
40 const terminate = () => {
41 voiceAIClient?.close();
42 VoxEngine.terminate();
43 };
44
45 call.addEventListener(CallEvents.Disconnected, terminate);
46 call.addEventListener(CallEvents.Failed, terminate);
47
48 try {
49 call.answer();
50
51 voiceAIClient = await OpenAI.createRealtimeAPIClient({
52 apiKey: VoxEngine.getSecretValue("OPENAI_API_KEY"),
53 model: "gpt-realtime-2.1",
54 onWebSocketClose: terminate,
55 });
56
57 voiceAIClient.addEventListener(OpenAI.RealtimeAPIEvents.SessionCreated, () => {
58 voiceAIClient.sessionUpdate(SESSION_CONFIG);
59 });
60
61 voiceAIClient.addEventListener(OpenAI.RealtimeAPIEvents.SessionUpdated, () => {
62 VoxEngine.sendMediaBetween(call, voiceAIClient); // media connected
63 voiceAIClient.conversationItemCreate({ // request a greeting
64 item: {
65 type: "message",
66 role: "user",
67 content: [
68 {
69 type: "input_text",
70 text: "The phone call just connected. "
71 + 'Say exactly: "Hello! How can I help today?"',
72 },
73 ],
74 },
75 });
76 voiceAIClient.responseCreate({}); // ask for the response now
77 });
78
79 // Barge-in: clear buffered audio when the caller starts speaking
80 voiceAIClient.addEventListener(OpenAI.RealtimeAPIEvents.InputAudioBufferSpeechStarted, () => {
81 Logger.write("===BARGE-IN: OpenAI.InputAudioBufferSpeechStarted===");
82 voiceAIClient.clearMediaBuffer();
83 });
84
85 voiceAIClient.addEventListener(
86 OpenAI.RealtimeAPIEvents.ConversationItemInputAudioTranscriptionCompleted,
87 (event) => {
88 const payload = eventPayload(event);
89 const transcript = payload.transcript || payload.text || payload.delta;
90 if (transcript) Logger.write(`===USER=== ${transcript}`);
91 },
92 );
93
94 voiceAIClient.addEventListener(
95 OpenAI.RealtimeAPIEvents.ResponseOutputAudioTranscriptDone,
96 (event) => {
97 const payload = eventPayload(event);
98 const transcript = payload.transcript || payload.text;
99 if (transcript) Logger.write(`===AGENT=== ${transcript}`);
100 },
101 );
102
103 // Consolidated "log-only" handlers - key OpenAI/VoxEngine debugging events
104 [
105 OpenAI.RealtimeAPIEvents.ResponseCreated,
106 OpenAI.RealtimeAPIEvents.ResponseDone,
107 OpenAI.RealtimeAPIEvents.ResponseOutputAudioDone,
108 OpenAI.RealtimeAPIEvents.ConnectorInformation,
109 OpenAI.RealtimeAPIEvents.HTTPResponse,
110 OpenAI.RealtimeAPIEvents.Error,
111 OpenAI.RealtimeAPIEvents.WebSocketError,
112 OpenAI.RealtimeAPIEvents.Unknown,
113 OpenAI.Events.WebSocketMediaStarted,
114 OpenAI.Events.WebSocketMediaEnded,
115 ].forEach((eventName) => {
116 voiceAIClient.addEventListener(eventName, (event) => {
117 Logger.write(`===${event.name}===`);
118 if (event?.data) Logger.write(JSON.stringify(event.data));
119 });
120 });
121 } catch (error) {
122 Logger.write("===UNHANDLED_ERROR===");
123 Logger.write(error);
124 terminate();
125 }
126});

More information