NumberGroup call webhooks explained: start-of-call, end-of-call, and the variable mechanism
How the webhook mechanism actually works — start-of-call hooks for routing decisions, end-of-call hooks for CRM logging, conditional firing, regex-based variable extraction, and how to wire it up to your own backend (or a Cloudflare Worker).
Most of the “can NumberGroup integrate with X?” questions we get have the same answer: yes — via webhooks. The mechanism is small, generic, and powerful enough to drive almost any CRM, helpdesk, analytics tool, or internal system you already run. This post is the foundational reference. The CRM-specific posts (HubSpot, Salesforce, Pipedrive, Zoho) all assume you’ve read this one.
The two hooks: start-of-call and end-of-call
Open any number in the portal, scroll to the Integrations panel, and you’ll see two dropdowns:
- Start of call webhook — fires the moment a call lands on the number, before routing happens. The HTTP response can set variables that influence routing.
- End of call webhook — fires when the call ends. Use this to log the call somewhere (a CRM, a spreadsheet, your own database).
The dropdowns are populated from the Web Hook Manager. A single webhook can be reused across many numbers — define once, attach everywhere.
Anatomy of a webhook
Each webhook has four sections: Details, Conditions, Results, and (implicitly) where you attach it.
Details — Name, URL, Post Body
- Name — what it shows up as in the dropdown. Be descriptive: “HubSpot — log inbound call” beats “Webhook 1”.
- URL — the HTTPS endpoint we’ll POST to. This is the receiver you control (a Cloudflare Worker, a Vercel function, a Lambda, a Rails endpoint, whatever).
- Post Body — optional. If you leave it blank we send our default payload. If you fill it in, we send that body with variable substitutions.
Conditions — when does it fire?
A webhook can be attached to a number but only fire under certain conditions. Each condition is “variable X is equal to Y”. Multiple conditions are ANDed together. Useful patterns:
- Only fire on calls from UK mobiles:
equalscaller_typemobile - Only fire during office hours: gate this in your receiver instead — easier to maintain
- Fire one webhook for sales DDIs, another for support DDIs: condition on the dialled number
If you want it to fire on every call, leave conditions empty.
Results — extracting variables from the response
This is the magic bit. Your webhook’s HTTP response body is searched with a regex you supply, and the first capture group is assigned to a variable name you supply. That variable is then available to routing and to subsequent webhooks in the same call.
Example: your start-of-call hook queries your CRM for whether the caller is a known VIP. Your worker responds:
{"vip":"yes","customer_id":"cus_8421"}In the Results section you set variable
is_vip from regex "vip":"(yes|no)", and variable cust_id from regex "customer_id":"([^"]+)". Now your routing translation panel can branch on is_vip == yes to send VIPs straight to your account manager instead of the main hunt group.What we send you
The default POST body contains the standard call metadata: caller number, dialled number, call ID, timestamp, and (where available) caller location and network type. If you supply a custom Post Body you can shape it however your receiver expects — JSON, form-encoded, XML — and reference call variables inside it.
Practical tip: before you write any receiver code, point the webhook at webhook.site and place a test call. You’ll see the exact JSON we send. Then build against that. This takes 2 minutes and saves an hour of guessing.
Where the receiver should live
We don’t care what you build the receiver in. Three patterns we see Members use most:
- Cloudflare Workers — global, ~5ms cold start, free tier covers millions of webhook calls. Our own AI call alerts run on Workers. Recommended if you’re starting from scratch.
- Vercel / Netlify functions — drop in an API route, ship. Good if you already have a Next.js / Nuxt app there.
- Your own backend — Rails, Django, Laravel, .NET, Node. Make sure the endpoint is HTTPS, public, and responds within ~2 seconds (we’ll time out otherwise, which matters for start-of-call hooks that gate routing).
The 2-second rule for start-of-call hooks
End-of-call hooks can take as long as they like — the call’s over. Start-of-call hooks block routing until you respond. If your receiver hits a slow CRM API and takes 6 seconds, the caller hears 6 seconds of ringing-but-nothing-happening. Two patterns to keep this snappy:
- Respond immediately with whatever you already know, then do the slow CRM write in the background.
- Keep a small in-memory cache (Workers KV, Redis) of recent caller lookups so the hot path is fast.
Security — verify it’s really us
Webhook URLs are HTTPS but otherwise public. If you want to be certain a POST came from NumberGroup and not somebody who guessed your endpoint:
- Put a shared-secret token in the URL itself, e.g.
— your worker checks the path segment and 401s if it’s wrong.https://your-worker.example.com/numbergroup/abc123-secret-xyz - Or put the secret in the Post Body. Same idea.
- Rotate the secret if you ever leak it. Just update the webhook URL in the portal.
We don’t currently sign payloads with HMAC. If you need that (e.g. for SOC 2 evidence), ring us — it’s on the roadmap.
End-to-end example: log every inbound call to a Google Sheet
Smallest possible useful integration. Cloudflare Worker that forwards the call event to a Google Sheets webhook (via Apps Script):
// worker.js
export default {
async fetch(req, env) {
if (req.method !== "POST") return new Response("ok");
const call = await req.json();
// Forward to Apps Script web app deployed against your sheet
await fetch(env.SHEET_WEBHOOK_URL, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
ts: new Date().toISOString(),
caller: call.caller || "",
dialled: call.called || "",
duration: call.duration || 0,
}),
});
return new Response("logged");
},
};Attach this as the end-of-call webhook on every DDI you want logged. Done. You’ve got a call log in a spreadsheet your whole team can see, in about 20 lines of code.
Next steps — connect a real CRM
The CRM-specific posts use the same pattern but talk to a real CRM API instead of a sheet:
Stuck wiring this up? Ring us on 0800 310 1010 or email help@numbergroup.com— we’ve done most of these before and can point you at working patterns.
Move your phones to us. We'll save you money.
Memorable numbers, unlimited UK numbers, every smart feature and a UK team — all included with Membership at £149/mo. No contract.