Receive SMS via webhook in 5 minutes
Stand up a webhook handler that receives inbound SMS from a DIDHub number, verifies the HMAC signature, and posts a formatted card to Slack. Working code in Node and Python; deployable as a Cloudflare Worker, AWS Lambda, or any HTTP server.
Step 1: Get your DIDHub webhook secret
Dashboard → Settings → API & Webhooks → Webhook secret. Copy the value — you'll need it to verify incoming requests. Treat it like a password; rotate any time without affecting message delivery.
Step 2: Write the handler
Node.js (Cloudflare Worker)
export default { async fetch(req, env) { if (req.method !== 'POST') return new Response('Method not allowed', { status: 405 }); // 1. Verify HMAC-SHA256 signature const raw = await req.text(); const sig = req.headers.get('x-didhub-signature') || ''; const key = await crypto.subtle.importKey( 'raw', new TextEncoder().encode(env.DIDHUB_WEBHOOK_SECRET), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'] ); const mac = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(raw)); const expected = [...new Uint8Array(mac)].map(b => b.toString(16).padStart(2, '0')).join(''); if (sig !== expected) return new Response('Bad signature', { status: 401 }); // 2. Parse + post to Slack const { from, to, body, country } = JSON.parse(raw); await fetch(env.SLACK_WEBHOOK_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: `SMS to ${to}`, blocks: [ { type: 'section', text: { type: 'mrkdwn', text: `*${from}* (${country}) → *${to}*\n> ${body}` } } ] }) }); return new Response('ok', { status: 200 }); } };
Python (FastAPI on Lambda)
from fastapi import FastAPI, Request, HTTPException import hmac, hashlib, os, httpx app = FastAPI() SECRET = os.environ["DIDHUB_WEBHOOK_SECRET"].encode() SLACK_URL = os.environ["SLACK_WEBHOOK_URL"] @app.post("/sms") async def handler(req: Request): raw = await req.body() sig = req.headers.get("x-didhub-signature", "") expected = hmac.new(SECRET, raw, hashlib.sha256).hexdigest() if not hmac.compare_digest(sig, expected): raise HTTPException(401) msg = await req.json() text = f"*{msg['from']}* ({msg['country']}) → *{msg['to']}*\n> {msg['body']}" async with httpx.AsyncClient() as c: await c.post(SLACK_URL, json={"text": msg["to"], "blocks": [{"type":"section", "text": {"type":"mrkdwn","text": text}}]}) return {"ok": True}
Step 3: Deploy
Deploy the handler to a public HTTPS URL. Examples:
- Cloudflare Worker —
wrangler deploy→ URL likehttps://your-worker.your-account.workers.dev. - AWS Lambda + API Gateway — deploy via SAM or Serverless Framework, get an API Gateway URL.
- Vercel / Netlify Functions —
vercel --prod→ project URL. - Self-hosted — any VPS with a TLS cert. Caddy or Nginx as the front-end.
Step 4: Wire it to your DID
Dashboard → Numbers → pick the DID → Inbound routing → SMS delivery → Webhook → paste your URL → Save.
Tick Forward delivery receipts if you also send outbound SMS from this DID and want DLR callbacks at the same URL.
Step 5: Test
Two ways to verify before sending a real SMS:
- Dashboard test button. Routing tab → Send test webhook → DIDHub fires a sample
sms.receivedpayload with a real signature. Your handler runs end-to-end. Slack should show the test message. - Real SMS. Text your DIDHub number from any phone — the message appears in Slack within ~1 second.
{verify: (req,res,buf) => req.rawBody = buf} to express.json (Node example above) or compute the HMAC over a re-serialised JSON. The latter is brittle — always sign the raw bytes.Production checklist
- Idempotency. Dedupe on
payload.idin Redis or a DB column. DIDHub may retry — you don't want to post to Slack twice. - Fast ACK. Respond 200 within 1-2 seconds. If real work takes longer, queue it and respond first.
- Error budget. Tolerate the occasional 5xx in your monitoring — DIDHub retries automatically. Page only on a sustained failure rate.
- Secret rotation. Rotate the webhook secret quarterly or after any suspected leak. Old + new are both accepted for 24h after rotation so you can deploy at your own pace.
- IP allowlist. Lock your firewall to DIDHub's published webhook origin range (
/.well-known/webhook-origins) for defense-in-depth. - Observability. Log
id,from,to, latency, and HMAC verification result. Bench against the dashboard's message log to catch drops.
What you just built
A production-ready SMS-to-Slack pipeline with HMAC-verified delivery, sub-second latency, and zero infrastructure overhead. The same handler skeleton extends to Zendesk ticket creation, Salesforce activity logging, AI auto-responders, or any custom flow — just swap the Slack POST for your real downstream system.
More tutorials
3CX SIP Trunk Setup with DIDHub
How to add a DIDHub SIP trunk in 3CX v18 / v20 using the generic SIP trunk template. DID inbound rule, outbound rule, and codec order includ
Asterisk pjsip Trunk to DIDHub
Minimal copy-pasteable pjsip.conf and extensions.conf for an Asterisk 18+ trunk registered to DIDHub with inbound + outbound routing.
Connect Vapi BYOC SIP Trunk to a DIDHub Phone Number
Step-by-step tutorial for Vapi BYOC: create a DIDHub SIP trunk, register it in Vapi, import a DID, and test inbound + outbound. Cuts Vapi te
SIP Trunk Failover Across DIDHub PoPs
Set up SIP trunk failover across DIDHub PoPs in 8 regions (US East/West, EU, MENA, India, APAC, ANZAC, LATAM, Africa) with DNS SRV, primary
Ready to get a number?
Pick a DID in 130+ countries from $1.99/month. Activates instantly on most numbers.