Lead webhook — push leads into your CRM
When a new lead is captured (or re-engaged after 30 days of silence), Wilow POSTs signed JSON to a URL you configure. Use this to drop leads straight into HubSpot, Pipedrive, a Google Sheet, your own CRM — anything that takes a POST.
When to enable it
Enable the webhook if a sales or support tool other than Wilow is the system of record for your leads. Without it you're copy-pasting rows from the admin Leads page into whatever tool runs the pipeline — tolerable for a handful, painful past a few a week.
Skip it if you're happy working directly from the admin Leads page. All lead data is visible and exportable there; the webhook is an integration convenience, not a prerequisite.
Configure
Admin → Widget → Lead webhook.
- URL — HTTPS only. We reject HTTP and SSRF-suspicious hosts at save time (private IPs, localhost,
.internalTLDs). - Secret — click Generate. We show it once; copy it to your destination immediately. If you lose it, regenerate — existing signatures become invalid.
- Custom headers (optional) — extra headers the destination needs, typically an API bearer token. Stored encrypted at rest (AES-256-GCM); you won't see the full value again after saving.
- Payload template (optional) — see below.
- Hit Test. We fire a synthetic lead at your endpoint and show the response code + timing. If it's anything other than 2xx, fix and retry before saving.
- Hit Save.
Default request shape
When no template is set, every request looks like this:
{
"event": "lead.created",
"tenantId": "…",
"leadId": "…",
"email": "[email protected]",
"phone": "+491234567890",
"conversationId": "…",
"capturedAt": "2026-04-22T10:15:00.000Z"
}Re-engagement events fire "event": "lead.re_engaged" with the same shape plus lastConversationAt.
Every request carries X-Wilow-Signature: sha256=<hex> — HMAC of the raw body using your secret. Verify server-side before trusting the payload.
Payload templates
If your destination expects a specific JSON shape (HubSpot's /crm/v3/objects/contacts endpoint, for example), write a template:
{
"properties": {
"email": "{{email}}",
"phone": "{{phone}}",
"firstname": "{{firstName}}",
"lead_source": "Wilow widget"
}
}Available placeholders: {{event}}, {{tenantId}}, {{leadId}}, {{email}}, {{phone}}, {{firstName}}, {{conversationId}}, {{capturedAt}}, {{lastConversationAt}}. Missing values render as an empty string. The templating is string-based — you can put placeholders inside strings, but object keys have to be literal.
Verifying the signature
See Webhook security for the full reference — signing scheme, headers (X-Wilow-Signature, X-Wilow-Event, X-Wilow-Delivery-Id), verification code in Node / Python / Go / Ruby, raw-body pitfalls per framework, and secret rotation. Implement the receiver against that page, then reuse the same code for the ticket webhook and any future Wilow webhook.
One-liner summary: HMAC-SHA256 of the raw request body, keyed by your secret, hex-encoded, sent as sha256=<hex>. Verify with a constant-time compare. Reject on mismatch with 401.
Pitfalls
- Turning the feature off preserves the URL — if we disable the lead webhook on your workspace, the URL + secret stay on your config but we stop delivering. Re-enabling turns it on again without re-entering anything.
- Failures on your endpoint are logged, not retried. A flaky CRM endpoint means missed leads — point us at something reliable. If you need retries, front your CRM with a queue you control.
- 2xx means delivered. We don't inspect the response body. If your CRM returns 200 but silently dropped the lead, that's invisible to us.
- Idempotency. Each lead fires once per unique visitor per contact. If we were to retry (we don't),
leadIdwould stay the same — dedupe on that on your side if you ever front the webhook with a queue.