Skip to main content
A WebRTC softphone inside ServiceNow. Agents call from a record or from the OpenFrame panel, talk in the browser, and the call is written back to the ServiceNow interaction table with a playable recording link. Source code: vobiz-ai/Vobiz-ServiceNow-Calling — the softphone, the OpenFrame mount, the Call via Vobiz UI Action, and the Node backend that answers Vobiz webhooks and writes to ServiceNow.
Scope: inbound and outbound, with browser audio over WebRTC. The backend, the answer XML and the whole HTTP surface are covered by npm test — 46 assertions, including a security-regression group. A live call has not been run since the 2.1.0 security rewrite, so verify both directions on your own account before rolling it out to agents.

What you get

How it works

The one thing to understand first: the browser is the A leg. The softphone sends the SIP INVITE itself, and the backend answers <Dial><Number> to reach the customer.
Build it this way round. Originating to the customer over the REST API first and then bridging the agent in with <Dial><User> does not work for a registered WebRTC endpoint — the customer answers, hears ringback, and then “the agent could not be reached”.Inbound is the exception: <Dial><User> is the only way to reach a registered endpoint, and it works today.
The backend writes the interaction record itself, over the Table API, using the instance credentials in .env. That is the opposite of the HubSpot build, where the CRM creates the engagement from the widget’s own session — so here the ServiceNow user in .env needs write access to interaction.

Requirements

Step 1: Prove the Vobiz account

Place a call with rtc-demo.vobiz.ai. If that fails, nothing here will work and you will debug the wrong layer for a day.

Step 2: Create the SIP endpoint

Vobiz rewrites the username you submit. Send snagent and the stored username comes back as something like snagent1187694299145202883643. Read the stored username out of the response — that is what registers, and what VOBIZ_SIP_USER must contain.

Step 3: Run the backend

cloudflared must be on your PATH (brew install cloudflared, or winget install --id Cloudflare.cloudflared -e). The binary is deliberately not committed. The .env values that matter:
Set SIGNING_SECRET in production. Left unset it defaults to a per-process random value, so every restart invalidates every playback link already written into a ServiceNow work note.
Then verify before touching ServiceNow:
The second must return <Response> containing <Dial …><Number>. Anything else — a tunnel error page, an ngrok interstitial — and every call dies silently.

Step 4: Point Vobiz at the backend

Create an application whose answer_url is $TUNNEL_URL/answer, bind the SIP endpoint to it, and attach a DID for inbound. POST /setup on the backend does this for you with an active session, or do it by hand:

Step 5: Set the ServiceNow properties

In System Properties → All Properties (sys_properties.list), create:
vobiz.calling.shared_secret must be type password2, not string. Any user who can list sys_properties can read a string property, and this value authorises outbound calls billed to your Vobiz account. Generate it with openssl rand -hex 32.If it is empty, the UI Action sends no X-Vobiz-Secret header and /start-call answers 401 unauthorized — that endpoint originates a billed call and is deliberately not open.

Step 6: Mount the OpenFrame panel

In the Filter Navigator go to OpenFrame → Configurations → New: The panel then appears in the top-right of the Next Experience header and loads the softphone inside the ServiceNow frame.

Step 7: Sign in and call

Open the OpenFrame panel. Before credentials are entered the badge reads CONNECTING and the three steps below are inert.
Vobiz Calling for ServiceNow panel showing a CONNECTING badge, a Vobiz account and SIP direct tab pair, empty Auth ID and Auth Token fields, and the numbered steps Sign in to Vobiz, Choose a caller ID and Place a call

The panel before sign-in.

Sign in with the Auth ID and Auth Token from the console under API credentials — or switch to SIP direct for an endpoint username, password and caller ID. Allow the microphone. The badge turns green when JsSIP has registered.
The panel showing a green READY badge, a Ready banner, the filled Auth ID and masked Auth Token, and a Logged in as line showing the redacted account ID and calling-from number

Registered. The badge reads READY and the signed-in account is shown.

Wait for the status to read Ready before dialling. That is the SIP registration landing; dialling before it rings the customer into silence.

Choose a caller ID

Calling from lists the numbers on the account. Carriers require a real number here to bridge a call to a mobile or landline.
The Calling from field expanded into a dropdown of five Vobiz numbers with the first highlighted, above the number-to-call field and a Call button

Selecting the outbound caller ID.

Place the call

Type a number in E.164 format, or click a number in ServiceNow and the panel picks it up. Recently used numbers are suggested as you type, in both their +91… and bare forms. Once connected the panel switches to Hang up, starts the timer, and confirms who is on the call.
The panel during a live call showing the destination number, a red Hang up button, a running timer and an On a call with banner, above the Receive calls here setup section

An active call.

From a record. The Call via Vobiz UI Action posts to /start-call with the shared secret from step 5.

Inbound calls

Receive calls here is the one-time setup that points your Vobiz number at this panel. After that, a call to the attached DID raises an in-panel prompt — Enter accepts, Escape declines.
The panel with an INCOMING CALL badge and a green incoming-call card showing the calling number with Accept and Decline buttons and the hint Enter to accept, Escape to decline

The inbound prompt, with keyboard shortcuts.

Recordings

Completed calls appear under Call recordings with their timestamp, each with an inline player.
The Call recordings section listing three timestamped entries, each with its own inline audio player and a refresh control in the section header

Recordings, with inline players.

The same link is written into the interaction work notes, signed and short-lived.

What lands in ServiceNow

One record per call in the interaction table:

Reading the logs

The backend log is the honest account of what happened:
DialBLegUUID is the single most useful field in this stack. Present means the call connected. Empty means no B leg was ever created, whatever the UI said.

Backend routes

Troubleshooting

Testing

Starts a backend against a mock Vobiz API, drives both call directions through /answer, checks the CDR ledger, and asserts the security regressions — that unsigned playback is refused, that /agent and /start-call demand authentication, that /health leaks nothing, and that an unknown origin is not echoed back as allowed. It exits non-zero when any of that stops being true.

Next steps