Building Blocks / Voice
Dialer / Softphone
Softphone in the browser. Click a contact, place the call.
Drop-in widget. Load
https://webforms.dropcowboy.com/latest/dropcowboy-dialer.min.jsand pass it a site token your server mints. Do not rebuild WebRTC calling, audio device handling, or caller ID selection; the widget already does it.Calling setup comes with the site token. Your page never configures SIP, STUN, or TURN servers; the script does.
When to use this vs REST
Use the Dialer when a person places calls from your app. For calls with no person on your side, use a campaign or the AI agent APIs.
Do not rebuild
- WebRTC calling, audio devices, mute, hold, or transfer.
- Caller ID choice. Local presence picks the number.
Drop-in
- On your server, trade your API key for a short-lived site token (the key needs
numbers:write):
// Your server. Browser code never sees KEY/SECRET.
app.post('/dropcowboy/token', async function (req, res) {
const r = await fetch('https://api-v2.dropcowboy.com/phone/public/embed/token', {
method: 'POST',
headers: {
'x-key': process.env.DC_KEY,
'x-secret': process.env.DC_SECRET,
'Content-Type': 'application/json'
},
body: JSON.stringify({ site_id: process.env.DC_SITE_ID, scope: ['dialer:webrtc'], ttl_seconds: 900 })
});
const body = await r.json();
if (!r.ok) return res.status(r.status).json(body);
// The API wraps results in data. Hand the browser just the token.
res.json({ token: body.data.token, expires_at: body.data.expires_at });
});
Or test the mint with cURL:
# On your server: trade your API key (needs numbers:write) for a site token. 1 hour max.
# site_id is one UUID per app; generate your own in place of the example.
curl -s -X POST https://api-v2.dropcowboy.com/phone/public/embed/token \
-H "x-key: $KEY" -H "x-secret: $SECRET" \
-H "Content-Type: application/json" \
-d '{"site_id":"00000000-0000-4000-8000-000000000000","scope":["dialer:webrtc"],"ttl_seconds":900}'
- Load the script, init with the token, and call a method:
<script src="https://webforms.dropcowboy.com/latest/dropcowboy-dialer.min.js"></script>
<script>
(async function () {
const minted = await fetch('/dropcowboy/token', { method: 'POST' }).then(function (r) { return r.json(); });
await DropCowboy.dialer.init({ token: minted.token });
DropCowboy.dialer.setTheme({ theme: 'LIGHT', primaryColor: '#009efb' });
DropCowboy.dialer.addCallEndedListener(function (event) {
saveCallToCrm({
phone: event.phone_number || window.currentContact.phone,
disposition: event.disposition,
duration: event.duration
});
});
document.getElementById('call-btn').addEventListener('click', function () {
DropCowboy.dialer.callPhone(window.currentContact.phone);
});
})();
</script>
Pin a version with SRI
/latest/ changes whenever we ship. To pin a release, load the same file from /vX.Y.Z/ and add its Subresource Integrity hash. Each release lists its hashes at https://webforms.dropcowboy.com/vX.Y.Z/building-blocks-manifest.json: take the integrity of the entry whose file is dropcowboy-dialer.js. It covers dropcowboy-dialer.min.js as well, because both names serve the same bytes.
<script src="https://webforms.dropcowboy.com/vX.Y.Z/dropcowboy-dialer.min.js"
integrity="sha384-INTEGRITY_FROM_MANIFEST"
crossorigin="anonymous"></script>
Do not put integrity on a /latest/ URL. The file changes under it and the browser will refuse to run it.
JS API / HTML tag
| Method | What it does |
|---|---|
init({ token, getToken }) |
Mints SIP over the embed JWT and registers the WebRTC UA. Optional getToken returns a fresh site token (or { token, expires_at }) from your server; it runs before the token expires and once after a 401. |
setToken(token) |
Swaps in a site token you minted yourself. The next request uses it. |
window event 'dc-session-expired' |
Fires when the token expired and could not be refreshed. Mint a new one and call setToken(). |
setTheme({ theme, primaryColor }) |
LIGHT/DARK plus accent. |
callPhone(e164, options) |
Places an outbound call to the E.164 number. |
addCallEndedListener(fn) -> stop |
Fires when the call ends. Use disposition to log the result on the CRM record. |
addErrorListener(fn) -> stop |
Fires on mint, register, or call errors. |
close() |
Hangs up and unregisters. |
Auth and scopes
Site token. Your API secret stays on your server.
Site token scope: dialer:webrtc. Mint it with POST https://api-v2.dropcowboy.com/phone/public/embed/token using an API key with numbers:write. Tokens last up to one hour.
Limits
- Site tokens last at most one hour (the default). Mint a fresh one per page load or session; do not cache one across users.
- Calls and texts need a connected carrier (BYOC) and prepaid balance. Without them the mint returns 402 or 403 for telephony scopes.
- Calls belong to the Drop Cowboy® user who created the API key that minted the site token. They need no dialer seat, and any number of your users can be on calls at once with tokens from the same key. A token minted from a key with no creator is refused with 403
acting_user_required. - To tell your users apart, pass your own user id as
subwhen you mint the token. Thecontact.call.*webhooks for the call carry it back asexternal_user_id, along withsite_id.
Related REST
Routes are on https://api-v2.dropcowboy.com.
- POST /phone/public/embed/token - mint the site token
- GET /phone/public/lines - list your numbers
Full reference: Embed site tokens.