Messenger (SMS/MMS/RCS)

Two-way SMS from your app, sent from your business number.

Drop-in widget. Load https://webforms.dropcowboy.com/latest/dropcowboy-messenger.min.js and pass it a site token your server mints. Do not rebuild the text thread view or sender selection; the widget already does it.

When to use this vs REST

Use Messenger when a person in your app is having an SMS conversation. Use POST /sms from your server for one-off or automated texts.

Do not rebuild

  • A thread view with delivery status.
  • Sender number selection and opt-out handling.

Drop-in

  1. 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.
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":"YOUR_SITE_ID","scope":["dialer:webrtc"],"ttl_seconds":900}'
  1. Load the script, init with the token, and call a method:
<script src="https://webforms.dropcowboy.com/latest/dropcowboy-messenger.min.js"></script>
<script>
(async function () {
  const minted = await fetch('/dropcowboy/token', { method: 'POST' }).then(function (r) { return r.json(); });
  await DropCowboy.messenger.init({
    token: minted.token,
    from: '+15125550100'
  });
  DropCowboy.messenger.setTheme({ theme: 'LIGHT', primaryColor: '#009efb' });

  DropCowboy.messenger.addMessageListener(function (event) {
    saveSmsToCrm(event.to, event.body, event.sms_id, event.direction);
  });

  document.getElementById('sms-send').addEventListener('click', function () {
    var contact = window.currentContact;
    DropCowboy.messenger.sendMessage(contact.phone, {
      body: document.getElementById('sms-body').value,
      contactId: contact.id,
      consentId: contact.consentId
    });
  });
})();
</script>

JS API / HTML tag

Method What it does
init({ token, from, consentId }) from is your business DID. Optional consentId is used on every send unless overridden.
setTheme({ theme, primaryColor }) LIGHT/DARK plus accent.
sendMessage(to, { body, from, contactId, consentId }) Sends SMS. Requires consentId. Returns the queued message.
addMessageListener(fn) -> stop Fires { to, body, sms_id, status, direction } after a send.
addErrorListener(fn) -> stop Fires on send or init errors.
close() Tears down the session.

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.
  • Outbound SMS needs a consent_id captured with Consent Capture for that phone number.

Routes are on https://api-v2.dropcowboy.com.

  • POST /sms - send a text from your server
  • POST /phone/public/embed/token - mint the site token

Full reference: Embed site tokens, Texts API.

Copy the code

The snippets on this page. Your server keeps the API key pair (KEY and SECRET for curl, DC_KEY and DC_SECRET for Node); the browser only sees the short-lived site token.

Load the Messenger
<script src="https://webforms.dropcowboy.com/latest/dropcowboy-messenger.min.js"></script>
<script>
(async function () {
  const minted = await fetch('/dropcowboy/token', { method: 'POST' }).then(function (r) { return r.json(); });
  await DropCowboy.messenger.init({
    token: minted.token,
    from: '+15125550100'
  });
  DropCowboy.messenger.setTheme({ theme: 'LIGHT', primaryColor: '#009efb' });

  DropCowboy.messenger.addMessageListener(function (event) {
    saveSmsToCrm(event.to, event.body, event.sms_id, event.direction);
  });

  document.getElementById('sms-send').addEventListener('click', function () {
    var contact = window.currentContact;
    DropCowboy.messenger.sendMessage(contact.phone, {
      body: document.getElementById('sms-body').value,
      contactId: contact.id,
      consentId: contact.consentId
    });
  });
})();
</script>
Mint a site token (your server)
// 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 });
});
Mint a site token (curl)
# On your server: trade your API key (needs numbers:write) for a site token. 1 hour max.
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":"YOUR_SITE_ID","scope":["dialer:webrtc"],"ttl_seconds":900}'