Nexafone Voice CPaaS
Programmatic Call Flow Reference
Authenticate, originate, and orchestrate calls with verbs like play, gather, and dial. You can copy, paste, and deploy in minutes, just make sure you have a webhook to receive the DTMF input, numbers where you receive the call and you have the audio files in your CDN/cloud.
1. Generate an Access Token
- Open the Nexafone portal and copy your accountId (top right).
- Go to Settings → API Client and generate
apiKey/apiSecret. - POST to
https://api.nexafone.com/auth/get-access-token/<accountId>with:
{
"apiKey": "ak_0mcl7jor8q4ek2r5debkg3",
"apiSecret": "as_kyozshnllrwsqqckmothirpxewdv45ih"
}For more information, please refer to the Get a Access Token API page.
2. Call the Voice API
Take the accessToken from the previous response and send the originate request:
The platform records the call, plays audio, gathers digits, and hits your webhook for IVR routing. For more information, please refer to the Voice service API page.
Simple Call Request Payload Blueprint (ClickToCall)
A simple payload to connect two parties directly. The platform calls the from number first, then bridges to the to number, and finally dials the target number specified in the action.
{
"from": "+911203135800",
"to": "096506xxxxx",
"accountId": "68b6b9f023fe9b8f89193652",
"recordCall": { "enable": true },
"action": {
"type": "dial",
"timeout": 15,
"data": { "number": "098178xxxxx" }
},
"timeout": 30
}Field Notes
- from / to: PSTN numbers in E.164 format.
- recordCall.enable: toggles session recording.
- timeout: leg timeout (seconds) for outbound dialing.
Dial Action
- type: must be
"dial"for ClickToCall. - timeout: seconds to wait for the dial leg to connect.
- data.number: target number to bridge the call to.
Advanced Call Request Payload Blueprint
This payload mirrors the production call we use for demos. Adjust audioUrl, action, and numbers as needed.
{
"from": "+911203135800",
"to": "096506xxxx",
"accountId": "68b6b9f023fe9b8f89193652",
"recordCall": { "enable": true },
"action": {
"type": "play",
"data": {
"audioUrl": "https://nexafone.com/audio-prompts/thanks_for_call_plz_wait.wav",
"action": {
"type": "gather",
"data": {
"audioUrl": "https://nexafone.com/audio-prompts/hey_welcome_press1_or_2.wav",
"maxDigits": 5,
"timeout": 100000,
"digitTimeout": 5000,
"terminator": "#",
"action": "https://synecdochical-shanae-unworshiping.ngrok-free.dev/api/handle-dtmf"
}
}
}
},
"timeout": 30
}Field Notes
- from / to: PSTN numbers in E.164.
- recordCall.enable: toggles session recording.
- timeout: leg timeout (seconds) for outbound dialing.
Gather Settings
- maxDigits: digits before completing.
- timeout / digitTimeout: ms for first digit and gaps.
- action: HTTPS webhook receiving the DTMF payload.
Verb Reference
Combine verbs to build IVRs, transfers, and announcements without leaving JSON.
PLAY
verbStream prerecorded audio to the callee. Chain to another verb by setting `action` inside `data`.
- • `audioUrl` (required): HTTPS media served from Nexafone or your CDN.
- • `action` (optional): next verb to execute after playback completes.
GATHER
verbCollect DTMF input and POST results to your webhook for routing decisions.
- • `maxDigits`: digits to capture before returning.
- • `timeout` / `digitTimeout`: first-digit and inter-digit limits (ms).
- • `terminator`: key that immediately ends collection.
- • `action`: HTTPS webhook that receives `{ callId, dtmf, ... }`.
DIAL
verbBridge the caller to an agent/SIP endpoint and optionally continue the IVR afterwards.
- • `number`: PSTN/SIP target.
- • `timeout`: leg timeout in seconds.
- • `moh`: enable music-on-hold while bridging.
- • `action`: verb executed after the dial leg finishes.
Example Express Webhook
Drop this into server.js, run npm install, and expose it with ngrok. It answers gather callbacks and routes IVR options instantly.
require("dotenv").config();
const express = require("express");
const app = express();
const PORT = process.env.PORT || 3010;
app.use(express.json());
app.post("/api/dial", (req, res) => {
res.json({
type: "dial",
data: { number: process.env.TO_NUMBER || "1001" }
});
});
app.post("/api/gather", (req, res) => {
res.json({
type: "gather",
data: {
maxDigits: 4,
channelId: req.body.to.channelId,
timeout: 10000,
action: "https://synecdochical-shanae-unworshiping.ngrok-free.dev/api/dial"
}
});
});
app.post("/api/handle-dtmf", (req, res) => {
const { dtmf } = req.body;
if (dtmf === "1") {
return res.json({
type: "play",
data: {
audioUrl: "https://nexafone.com/audio-prompts/input_submitted_have_a_nice_day.wav",
action: { type: "hangup", data: {} }
}
});
}
if (dtmf === "2") {
return res.json({
type: "dial",
timeout: 15,
moh: { enable: true },
data: {
number: "072178xxxxx",
action: {
type: "play",
data: {
audioUrl: "https://nexafone.com/audio-prompts/nice_talking_give_your_feedback.wav",
action: { type: "hangup", data: {} }
}
}
}
});
}
return res.json({
type: "play",
data: {
audioUrl: "https://nexafone.com/audio-prompts/invalid_option.wav",
action: { type: "hangup", data: {} }
}
});
});
app.listen(PORT, () => {
console.log(`Server ready on http://localhost:${PORT}`);
});Environment
Copy `.env.sample` → `.env` and set `PORT` plus a default `TO_NUMBER`. Start locally with `npm start` or run `pm2 start ecosystem.config.js` in production.
Webhook Contract
Gather requests arrive as { callId, dtmf, to: { channelId } }. Use channelId if you need to resume media on the same leg.
Testing Checklist
- • POST the originate payload and confirm a 202/200 response before validating media actions.
- • Expose your Express server via ngrok/Cloudflare tunnel so Nexafone can reach /api/* webhooks.
- • Press DTMF options 1 and 2 to verify branching (play→hangup vs dial→play→hangup).
- • Allow the gather verb to timeout and confirm the fallback play verb executes.
- • Verify referenced audio files are reachable over HTTPS before running live calls.
Need More Verbs?
Talk to us about custom actions, speech recognition, and outbound campaigns tailored to your vertical.