> For a complete documentation index, fetch https://docs.voximplant.ai/llms.txt

# Example: Placing an outbound call

> This example starts a VoxEngine session, places an outbound PSTN call, and connects the callee to Qwen Cloud after they answer.

> For the complete documentation index, see [llms.txt](/llms.txt).

## Overview

This example starts a VoxEngine session, places an outbound PSTN call, and connects the callee to Qwen Cloud after they answer.

**⬇️ Jump to the [Full VoxEngine scenario](#full-voxengine-scenario).**

## Prerequisites

* A Qwen Cloud API key stored in Voximplant [Secrets](/platform/voxengine/secrets) under `QWEN_API_KEY`.
* The example uses `qwen-audio-3.1-realtime-plus`. Change `QWEN_MODEL` if your account should use another realtime model.
* A verified or rented caller ID that can place outbound PSTN calls.
* Start this scenario via a routing rule. For production, start the rule with the Management API.

## Custom data

Pass the destination and caller ID when starting the scenario:

```json
{"destination":"+15551234567","callerId":"+13322776633"}
```

## Alternate outbound destinations

This example uses `VoxEngine.callPSTN(...)` for PSTN dialing. You can also route outbound calls to other destination types in VoxEngine:

* **SIP** (`VoxEngine.callSIP`): dial a SIP URI to reach a PBX, carrier, SIP trunk, or other SIP endpoint.
* **WhatsApp** (`VoxEngine.callWhatsappUser`): place a WhatsApp Business-initiated call.
* **Voximplant users** (`VoxEngine.callUser`): call another app user in the same Voximplant application, such as a Web SDK, mobile SDK, or SIP user.

Relevant guides:

* [SIP calling options](/getting-started/network-options/sip)
* [Voximplant users calling options](/getting-started/network-options/web-mobile)
* [WhatsApp calling options](/getting-started/network-options/whatsapp)

## Connect call audio

Create the Qwen client inside `CallEvents.Connected`. After `SessionUpdated`, bridge the call:

```js
VoxEngine.sendMediaBetween(call, voiceAIClient);
```

> **Media bridge**
>
> `QwenCloud.VoiceAgentAPIClient` is not in `VoxMediaUnitMap` yet. `sendMediaBetween` matches the other realtime connectors. See [Overview](overview) for the typings gap.

## Notes

[See the VoxEngine API Reference for more details](/api-reference/voxengine/qwen-cloud).

## Full VoxEngine scenario

```javascript title={"voxeengine-qwen-place-outbound-call.js"} maxLines={0}
require(Modules.QwenCloud);

const QWEN_MODEL = "qwen-audio-3.1-realtime-plus";
const SYSTEM_PROMPT = `
  You are Voxi, a concise phone agent for Voximplant callers.
  Voximplant is pronounced VOX-im-plant.
  Reply in English. Keep answers brief and helpful.
  If you do not know, say so and offer to connect them to a human.
`;

const SESSION_PARAMETERS = {
    session: {
        modalities: ["text", "audio"],
        voice: "beth_v3.1",
        instructions: SYSTEM_PROMPT,
        input_audio_transcription: {language: "en"},
        output_audio: {language: "en"},
        turn_detection: {type: "server_vad"},
    },
};

function parseCustomData() {
    const raw = VoxEngine.customData();
    if (!raw) return {};
    try {
        return JSON.parse(raw);
    } catch (error) {
        Logger.write("===CUSTOM_DATA_PARSE_FAILED===");
        Logger.write(error);
        return {};
    }
}

VoxEngine.addEventListener(AppEvents.Started, () => {
    let voiceAIClient;
    const {destination, callerId} = parseCustomData();

    if (!destination || !callerId) {
        Logger.write(
            "Missing destination or callerId. Provide custom data: {\"destination\":\"+15551234567\",\"callerId\":\"+13322776633\"}",
        );
        VoxEngine.terminate();
        return;
    }

    const call = VoxEngine.callPSTN(destination, callerId);
    // Alternative outbound paths (uncomment one and comment out callPSTN):
    // const call = VoxEngine.callUser({username: destination, callerid: callerId});
    // const call = VoxEngine.callSIP(`sip:${destination}@your-sip-domain`, callerId);
    // const call = VoxEngine.callWhatsappUser({number: destination, callerid: callerId});

    const terminate = (event) => {
        if (event) Logger.write(JSON.stringify(event));
        voiceAIClient?.close();
        VoxEngine.terminate();
    };

    call.addEventListener(CallEvents.Disconnected, terminate);
    call.addEventListener(CallEvents.Failed, terminate);

    call.addEventListener(CallEvents.Connected, async () => {
        try {
            const providerApiKey = VoxEngine.getSecretValue("QWEN_API_KEY");
            if (!providerApiKey) throw new Error("Set QWEN_API_KEY in Voximplant Secrets");

            voiceAIClient = await QwenCloud.createVoiceAgentAPIClient({
                apiKey: providerApiKey,
                model: QWEN_MODEL,
                onWebSocketClose: terminate,
            });

            voiceAIClient.addEventListener(QwenCloud.VoiceAgentAPIEvents.SessionCreated, (event) => {
                Logger.write(`===${event.name}===>${JSON.stringify(event.data)}`);
                voiceAIClient.sessionUpdate(SESSION_PARAMETERS);
            });

            voiceAIClient.addEventListener(QwenCloud.VoiceAgentAPIEvents.SessionUpdated, (event) => {
                Logger.write(`===${event.name}===>${JSON.stringify(event.data)}`);
                VoxEngine.sendMediaBetween(call, voiceAIClient);
                voiceAIClient.conversationItemCreate({
                    item: {
                        type: "message",
                        role: "user",
                        content: [{
                            type: "input_text",
                            text: "The call just connected. Greet the caller in one short sentence.",
                        }],
                    },
                });
                voiceAIClient.responseCreate({});
            });

            voiceAIClient.addEventListener(
                QwenCloud.VoiceAgentAPIEvents.InputAudioBufferSpeechStarted,
                (event) => {
                    Logger.write(`===${event.name}===>${JSON.stringify(event.data)}`);
                    voiceAIClient.clearMediaBuffer();
                },
            );

            [
                QwenCloud.Events.WebSocketMediaStarted,
                QwenCloud.Events.WebSocketMediaEnded,
                QwenCloud.VoiceAgentAPIEvents.ConnectorInformation,
                QwenCloud.VoiceAgentAPIEvents.ResponseCreated,
                QwenCloud.VoiceAgentAPIEvents.ResponseDone,
                QwenCloud.VoiceAgentAPIEvents.ResponseAudioTranscriptDone,
                QwenCloud.VoiceAgentAPIEvents.ConversationItemInputAudioTranscriptionCompleted,
                QwenCloud.VoiceAgentAPIEvents.Error,
                QwenCloud.VoiceAgentAPIEvents.WebSocketError,
                QwenCloud.VoiceAgentAPIEvents.Unknown,
            ].forEach((evtName) => {
                voiceAIClient.addEventListener(evtName, (e) => {
                    Logger.write(`===${e.name}===>${JSON.stringify(e)}`);
                });
            });
        } catch (error) {
            Logger.write("===SOMETHING_WENT_WRONG===");
            Logger.write(JSON.stringify(error));
            VoxEngine.terminate();
        }
    });
});

```