What you get
How it works
The one thing to understand first: the browser is the A leg. The widget sends the SIP INVITE itself, and the backend answers<Dial><Number> to reach the customer.
Requirements
Step 1: Prove the Vobiz account
Place a call with rtc-demo.vobiz.ai before anything else. If that fails, nothing here will work and you will spend a day debugging the wrong layer.Step 2: Create the SIP endpoint
Step 3: Run the backend
PUBLIC_BASE and restart. Then check the answer URL before touching any UI:
<Response> containing <Dial …><Number>. A dead answer URL produces the exact symptom people blame on registration: the customer answers, hears ringback, then “the agent could not be reached”.
Step 4: Point Vobiz at the backend
With the widget signed in, one call does all three steps — creates the application, binds the endpoint, attaches the DID:- The endpoint-binding field is
app_id, not the documentedapplication, which is silently ignored and still returns202 "changed". - DID attachment is
POST /numbers/%2B<e164>/applicationwith{"application_id": …}. The+must be percent-encoded, and/Number/returns a bare401 Unauthorisedthat reads exactly like a credentials problem and is not one.
Step 5: Sign in and call
Open the widget in HubSpot. It opens on a CONNECTING badge while the SIP stack registers.
The widget on load — CONNECTING, with the sign-in form beneath it.

Signed in. The badge reads READY and the endpoint shows as ACTIVE.
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.
Every provisioned number on the account is selectable as the outbound caller ID.
Place the call
Type a number in E.164 format, or click any phone number in HubSpot and the panel dials it.
Numbers found in HubSpot are offered as suggestions in the dial field.

An active outbound call, with the elapsed timer.
Inbound calls
A call to the attached DID raises an overlay over HubSpot. Enter accepts, Escape declines.
The inbound overlay, with keyboard shortcuts.
Recordings
Completed calls appear under Call recordings with their duration and timestamp. Play opens an inline audio player.
Recordings list with the inline player open.
url 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.Provisioning a dedicated app and number
Rather than wiring the application by hand, the widget can create a dedicated Vobiz application and SIP endpoint for HubSpot and link a number to it, with the answer and hangup URLs already set.
Provisioning a dedicated application and number from the widget.
How a call reaches the CRM
HubSpot creates the engagement itself, from the SDK messages the widget sends —cti.outgoingCall, cti.incomingCall and cti.callCompleted — using the agent’s own session.
hubspotFetch in backend/hubspot-oauth.js has no callers. If server-side CRM writes are ever added, both the scopes and the install permissions change.Backend routes
The answer XML
Troubleshooting

A failed call reports its cause inline, under the dial field.
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 showed. It is logged on every /dial-status line.Ignore the Endpoint API’s sip_registered — it reads "false" even when registration genuinely succeeded, on every endpoint on the account. The widget gates on JsSIP’s registered event instead.Known limits
- The backend is single-account. It is bound to one Vobiz account through
.env, and every agent who signs in shares one SIP endpoint. Two agents on one install register as the same endpoint and race for calls.agentIdis self-asserted — it selects a label, not an identity. - Marketplace distribution is not set up.
distributionisprivate; switching tomarketplaceneeds listing assets that do not exist yet. agents.example.jsonis vestigial. SIP credentials come from.env; nothing reads that file for routing.
Next steps
- Clone the app: vobiz-ai/internal-hubspot-crm
- The same softphone in other CRMs: Zendesk, Pipedrive, Freshdesk
- Reference:
<Dial>,<Record>, SIP endpoints, CDRs