Webhooks
Push call and CRM events into your own systems the moment they happen — signed, retried and logged.
On this page
A webhook is Vocallie telling your systems that something happened. You give us a URL and pick the events you care about; we POST a JSON payload to that URL each time one fires.
Account → WebhooksWhat people use them for
- Sync into your own CRM when a call completes or a contact changes stage.
- Post into Slack or Teams when a booking is made or a call goes badly.
- Trigger a Zapier or Make scenario — a single catch hook subscribed to everything works well.
- Update a spreadsheet or dashboard in real time.
- Kick off your own follow-up processes — send an SMS, create a ticket, raise an invoice.
Setting one up
- 1
Get a URL that can receive a POST
Your own endpoint, a Zapier catch hook, a Make webhook — anything that answers with a 2xx status.
- 2
Add the webhook and pick events
Paste the URL and tick the events, grouped by category. There's a select all shortcut for a catch-all integration.
- 3
Send a test event
The send button fires a real delivery through the real signing and delivery path — so if the test arrives and verifies, production will too. Test payloads carry a
test: trueflag so your handler can ignore them. - 4
Verify the signature in your handler
See below. Skipping this means anyone who learns your URL can send you fake events.
The events
| Category | Events |
|---|---|
| Calls | call.completed, call.missed |
| Contacts | lead.created, lead.updated, lead.stage_changed, lead.deleted, note.created |
| Follow-ups | followup.scheduled, followup.completed, followup.failed, followup.cancelled |
| Callbacks | callback.queued, callback.completed, callback.failed, callback_request.created |
| Appointments | appointment.booked, appointment.completed, appointment.cancelled |
| Campaigns | campaign.started, campaign.lead_called, campaign.lead_failed, campaign.completed |
| Account | automation.paused |
The panel has an expandable payload reference showing the exact JSON for each event, so you can build your handler without guessing.
The payload
Every delivery has the same envelope:
{
"event": "call.completed",
"created_at": "2026-07-25T12:00:00Z",
"data": { }
}
with these headers:
| Header | Purpose |
|---|---|
x-webhook-signature | sha256= followed by the HMAC of the body. |
x-webhook-event | The event name, so you can route without parsing. |
user-agent | Vocallie-Webhooks/1 |
Verifying the signature
Each webhook has a signing secret starting whsec_, copyable from the panel. Compute an HMAC-SHA256
of the raw request body using that secret and compare it to the header.
Two things that trip people up:
- Sign the raw body, before any JSON parsing or re-serialising. Re-encoding changes the bytes and the signature won't match.
- Compare in constant time if your language offers it.
Careful
Anyone with the Webhooks permission can read every signing secret in your workspace. Treat that permission as sensitive when assigning team roles.
Delivery behaviour
Worth knowing before you rely on it:
- Only a 2xx counts as success. Anything else is a failure and gets retried.
- Three attempts, spaced roughly half a second and one second apart. That's it.
- Redirects are refused. If your endpoint returns a 3xx it counts as a failure — give us the final URL.
- The timeout is about 8 seconds. Acknowledge quickly and do your slow work afterwards.
- Deliveries never delay anything in Vocallie. A slow or broken endpoint can't hold up a call.
Careful
Retries are shallow and in-memory. An endpoint that's down for ten minutes will permanently miss those events — there's no dead-letter queue and no manual re-drive. If you need guaranteed delivery, have your handler acknowledge immediately into your own queue, and reconcile periodically using the API.
The delivery log
The page lists recent attempts with the event, the target URL, the HTTP status, any error and a timestamp. It's the first place to look when an integration goes quiet.
| What you see | What it means |
|---|---|
| Success | Your endpoint returned 2xx. |
| Failed with an HTTP code | Your endpoint answered, but not with a 2xx. |
| Failed with an error message | We couldn't reach it — DNS, timeout, TLS, or a blocked address. |
| Pending and stuck | Delivery was interrupted. Rare; the event is gone. |
Security
Your URL is treated as untrusted, so deliveries go through a guarded HTTP client that refuses private, internal and loopback addresses — including ones reached through DNS or a redirect. A webhook pointed at something internal fails with a "blocked" error rather than going through.
Volume and quotas
Events count against a monthly allowance on your plan, and they only count when a webhook is actually subscribed — an event nobody listens to costs nothing.
Watch out for per-contact events: a 250-contact campaign delivers 250
campaign.lead_called events. Subscribe to what you'll act on, not everything, unless you're on a
plan with headroom. More events are available as an add-on.
Webhooks vs automations
They react to the same events. Use automations when the work happens inside Vocallie — moving a stage, adding a note, scheduling a follow-up. Use webhooks when another system needs to know.
Plenty of workspaces use both on the same event.