Skip to main content
A browser softphone that logs every call to Pipedrive. Agents dial from the page, talk in the tab, and the call lands in the CRM as an Activity and a Call Log with duration and a signed recording link. Source code: vobiz-ai/internal-pipedrive-crm — the softphone, the embedded CTI panel, and the Node backend that answers Vobiz webhooks and syncs to Pipedrive.
Scope: inbound and outbound, with browser audio over WebRTC. Verified end to end against a live Vobiz account and a live Pipedrive sandbox — both directions connect with two-way audio and activities appear in the CRM.

What you get

How it works

The one thing to understand first: the browser is the A leg. The page sends the SIP INVITE itself, and the backend answers <Dial><Number> to reach the customer.
The intuitive design does not work, and is what this app used to do. Originating to the customer over the REST API and then bridging the agent in with <Dial><User> is blocked platform-side: Vobiz builds a gateway URI it cannot parse and drops its own INVITE (tr_eval_uri(): invalid uri, blocking gw). The customer answers, hears ringback, 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.

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 pdagent and the stored username comes back as something like pdagent1187694299145202883643. Read the stored username out of the response — that is what registers, and what VOBIZ_SIP_USER must contain.

Step 3: Run the backend

Put the tunnel URL in TUNNEL_URL, then check the answer URL is alive before touching any UI:
You must get <Response> containing <Dial …><Number>. Anything else — an ngrok interstitial, a 404 page — and every call dies.

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:

Step 5: Connect Pipedrive over OAuth

In Developer Hub → Create public app, set the callback URL to $TUNNEL_URL/auth/pipedrive/callback — it must match PIPEDRIVE_REDIRECT_URI exactly, and only one callback URL is allowed per app. Request these scopes and nothing more. Over-scoping is a documented rejection reason: Put the Client ID and Secret in .env, visit $TUNNEL_URL/auth/pipedrive and approve. GET /auth/pipedrive/status should then report "connected": true.

Step 6: Sign in and call

Open the panel. Before credentials are entered it shows NOT SIGNED IN.
Vobiz Calling for Pipedrive panel showing a NOT SIGNED IN badge, a Not signed in banner, and step 1 Sign in to Vobiz with Auth ID, Auth Token and Agent profile fields

The panel before sign-in.

Sign in with the Auth ID and Auth Token from the console under API credentials. The badge turns green when JsSIP has registered.
The panel showing a green READY badge, a Ready banner, the filled Auth ID and Auth Token fields, an Agent profile selector, and a Signed in as SA_LAUUF1D9 line

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

Wait for Ready before dialling. The Call button stays disabled until JsSIP registers, deliberately — dialling with SIP down 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 Choose a caller ID field expanded into a dropdown of six Vobiz numbers with one highlighted, above the dialpad

Selecting the outbound caller ID.

Place the call

Type a number in E.164 format or use the built-in dialpad.
The Place a call step with a destination number typed into the Number to call field and a numeric dialpad below it

Entering the destination number on the dialpad.

Once connected the panel shows the timer and the in-call controls — Mute, Hold and Transfer — with the matched Pipedrive contact underneath.
Live call view showing a red Hang up button, a Calling status line with a ten-second timer, Mute Hold and Transfer buttons, and a Pipedrive CRM contact panel below

An active call, with in-call controls and the matched CRM contact.

Inbound calls

A call to the attached DID raises an in-panel prompt. Enter accepts, Escape declines.
The panel with an ON CALL badge, a Ringing banner, and a green Incoming call card showing the calling number with Accept and Decline buttons

The inbound prompt, with keyboard shortcuts.

When the call ends

A summary confirms the duration and — the part that matters — that the call reached the CRM.
Call Finished notification card showing an Inbound call completed badge, the number, duration and finish time, and a green Synced to Pipedrive CRM Activity and Call Log confirmation

Call finished, with the CRM sync confirmed on the card itself.

Recordings are listed with their timestamp and duration, each with its own inline player.
Call recordings list showing three timestamped entries with durations, two of them expanded into inline audio players, above Activity log and Frequently asked sections

Recordings, with inline players.

Backend routes

Testing

The suite reports PASS / FAIL / SKIP and exits non-zero on failure. A fully configured run is 9 passed, 0 failed, 0 skipped.
SKIP is not a pass. It means a service is not configured, and the summary says so. Mocks are opt-in (ALLOW_MOCK=1) and label themselves — they used to be the silent fallback, which made the whole suite pass against a CRM it had never contacted.

Security

  • Recording playback is HMAC-signed with a short expiry and carries no credentials. It previously took a url parameter and fetched it with the account’s Vobiz credentials attached — a credential-exfiltration primitive any web page could drive. Never reintroduce a caller-supplied URL here.
  • The browser never holds the Vobiz Auth Token. It is POSTed once at sign-in and exchanged for an opaque session token.
  • CORS is an allowlist, not *.
  • OAuth tokens live in backend/.pipedrive-tokens.json, mode 0600, gitignored. That file holds a refresh token — a long-lived credential for the customer’s entire CRM.

Troubleshooting

The backend log is the honest account. The field that matters:
DialBLegUUID is the single most useful field in this stack. Present means it connected; empty means no B leg was ever created, whatever the UI showed.
Three client settings in agent-phone/vobiz-sip.js are not optional, and all three fail in ways that point nowhere near the cause:
Ignore the Endpoint API’s sip_registered. It reads "false" even when registration genuinely succeeded, on every endpoint on the account. The panel gates on JsSIP’s registered event instead.Quick tunnels are the most common cause of “it stopped working.” The hostname changes on every restart, and TUNNEL_URL, the Vobiz application’s answer_url and the Pipedrive OAuth callback all go stale together.

Next steps