Webhooks: know what happened to every send
Drop Cowboy® posts a signed JSON event to your server when a voicemail lands, a text fails, a contact replies or an AI agent finishes a call.
Set up in four steps
Subscribe
Call POST /register/public/webhooks once per event type, or use Developers > Webhooks in the dashboard. Several event types can share one URL.
Store the signing secret
Each subscription returns a signing_secret. Subscribing to the same event type again replaces the URL and issues a new secret.
Verify every delivery
X-Signature is sha256= plus an HMAC-SHA256 of the X-Timestamp value, a dot, and the raw body. Verify the raw bytes and reject old timestamps.
Answer fast, work later
Return any 2xx within 5 seconds and process from a queue. Deduplicate on event_id, which is the same on every attempt.
Subscribe and verify
curl -X POST https://api-v2.dropcowboy.com/register/public/webhooks \
-H "x-key: $DC_KEY" -H "x-secret: $DC_SECRET" \
-H "Content-Type: application/json" \
-d '{
"hook_type": "contact.rvm.status",
"hook_url": "https://hooks.example.com/dropcowboy"
}'const crypto = require('crypto');
function verifyWebhook(rawBody, headers, secret) {
const signature = headers['x-signature'];
const timestamp = headers['x-timestamp'];
if (!signature || !timestamp) return false;
// Reject deliveries signed more than 5 minutes ago.
const age = Math.abs(Date.now() / 1000 - Number(timestamp));
if (!Number.isFinite(age) || age > 300) return false;
const expected = 'sha256=' + crypto.createHmac('sha256', secret)
.update(timestamp + '.')
.update(rawBody)
.digest('hex');
const a = Buffer.from(signature);
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}Events you can subscribe to
GET /register/public/events returns the live list.
Voice sends
contact.rvm.status for ringless voicemail, voice broadcast and AI calls; contact.rvm.receipt for proof of delivery.
Texts and replies
contact.sms.status for every text, contact.msg.received for inbound messages and contact.msg.opt_out when a contact replies STOP.
contact.email.status for delivery, opens and clicks, plus bounce, hard bounce and complaint events.
AI agents
ai_agent.call.completed, ai_agent.call.failed and ai_agent.outcome.* events when an agent finishes a call.
Campaigns
campaign.started, campaign.paused and campaign.completed, alongside the per-contact status events.
Contacts and more
Contact, list, consent, call, appointment, form, pipeline and chat events. Bulk changes arrive as one event per operation.