Webhook Integration
Leads by Alex fires a signed HTTP POST to your endpoint every time a new lead is added. Connect to Zapier, Make, HubSpot, Follow Up Boss, or any custom system — no polling required.
Payload delivered within seconds of lead insertion.
HMAC-SHA256 signature on every request. Verify authenticity server-side.
Works with Zapier, Make, n8n, or your own server.
Overview
When a new lead matches your subscription and is inserted into the database, Leads by Alex sends a POST request to every active endpoint you have configured.
Each request contains a JSON body (the payload) and an X-Webhook-Signature header you can use to verify the request came from us.
You can optionally attach a filter to an endpoint so it only fires for leads that match specific counties or lead types (probate, divorce, foreclosure, eviction).
If you enable auto-skiptrace on a saved filter, any matching lead will be automatically skiptraced before the webhook fires — phone numbers and email addresses will be included in the data object. Skiptrace credits are consumed per enriched lead.
Payload schema
All requests use Content-Type: application/json.
{
"event": "lead.created",
"timestamp": 1740000000000,
"data": {
"id": 12345,
"case_number": "2026PR000031",
"lead_type": "probate",
"county": "Brown",
"state": "WI",
"owner_name": "John Smith",
"property_address": "123 Main St, Green Bay, WI 54301",
"mailing_address": "456 Oak Ave, Green Bay, WI 54301",
"filing_date": "2026-02-01",
"status": "new",
"created_at": "2026-02-01T12:00:00.000Z",
// Present only when a matching auto-skiptrace filter fired:
"primary_phone": "(920) 555-0100",
"mobile_1": "(920) 555-0101",
"mobile_2": null,
"landline_1": null,
"email_1": "john.smith@example.com",
"email_2": null
}
}| Field | Type | Description |
|---|---|---|
| event | string | Always lead.created |
| timestamp | number | Unix milliseconds when the event was generated |
| data.id | number | Internal lead ID |
| data.case_number | string | WCCA case number, e.g. 2026PR000031 |
| data.lead_type | string | One of: probate, divorce, foreclosure, eviction |
| data.county | string | Wisconsin county name, e.g. Brown |
| data.state | string | Always WI (Wisconsin) |
| data.owner_name | string | Primary party name (owner, decedent, defendant, etc.) |
| data.property_address | string | Property or associated address |
| data.mailing_address | string | null | Mailing address if different from property |
| data.filing_date | string | WCCA filing date in YYYY-MM-DD format |
| data.status | string | Always new on creation |
| data.created_at | string | ISO-8601 timestamp when the lead was inserted |
| data.primary_phone | string | null | Phone number — included when an auto-skiptrace filter matched. null if not found or skiptrace not triggered. |
| data.mobile_1 … mobile_5 | string | null | Additional mobile numbers from skiptrace, if available |
| data.landline_1 … landline_3 | string | null | Landline numbers from skiptrace, if available |
| data.email_1 … email_5 | string | null | Email addresses from skiptrace, if available |
Signature verification
Always verify the signature before processing a webhook. This prevents attackers from forging requests to your endpoint.
Every request includes an X-Webhook-Signature header:
X-Webhook-Signature: t=1740000000000,v1=a3f2...c9d1The signature is constructed as:
- Extract
t(timestamp in ms) andv1(HMAC) from the header. - Build the signed string:
${t}.${rawBody}(timestamp, literal dot, raw JSON body — no newlines or extra whitespace). - Compute
HMAC-SHA256(secret, signedString)and compare tov1using a constant-time equality function. - Optionally reject payloads where
|now - t| > 300_000ms (5 minutes) to prevent replay attacks.
Your per-endpoint secret is shown in the Automations dashboard. Treat it like a password — never expose it client-side.
Code examples
Node.js / TypeScript
import crypto from "crypto";
export function verifyWebhookSignature(
rawBody: string,
signature: string, // from X-Webhook-Signature header
secret: string,
): boolean {
// Parse "t=<ms>,v1=<hmac>"
const parts = Object.fromEntries(
signature.split(",").map((p) => p.split("=", 2))
);
const timestamp = parts["t"];
const hmac = parts["v1"];
if (!timestamp || !hmac) return false;
// Reject replays older than 5 minutes
if (Date.now() - Number(timestamp) > 5 * 60 * 1000) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(hmac),
Buffer.from(expected),
);
}Python
import hmac
import hashlib
import time
def verify_webhook_signature(raw_body: bytes, signature: str, secret: str) -> bool:
"""Verify the X-Webhook-Signature header from Leads by Alex."""
# Parse "t=<ms>,v1=<hmac>"
parts = dict(p.split("=", 1) for p in signature.split(",") if "=" in p)
timestamp = parts.get("t")
received_hmac = parts.get("v1")
if not timestamp or not received_hmac:
return False
# Reject replays older than 5 minutes
if abs(time.time() * 1000 - int(timestamp)) > 5 * 60 * 1000:
return False
signed_payload = f"{timestamp}.{raw_body.decode('utf-8')}"
expected = hmac.new(
secret.encode("utf-8"),
signed_payload.encode("utf-8"),
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(received_hmac, expected)PHP
<?php
function verifyWebhookSignature(
string $rawBody,
string $signature,
string $secret
): bool {
// Parse "t=<ms>,v1=<hmac>"
$parts = [];
foreach (explode(',', $signature) as $part) {
[$k, $v] = explode('=', $part, 2);
$parts[$k] = $v;
}
if (empty($parts['t']) || empty($parts['v1'])) return false;
// Reject replays older than 5 minutes
if (abs(microtime(true) * 1000 - (int)$parts['t']) > 5 * 60 * 1000) {
return false;
}
$expected = hash_hmac('sha256', $parts['t'] . '.' . $rawBody, $secret);
return hash_equals($expected, $parts['v1']);
}Integrations
Use the "Webhooks by Zapier" trigger (Catch Hook). Requires Zapier Professional or higher.
Use a "Webhooks" module as the trigger in any Make scenario.
Add a "Webhook" node as your workflow trigger.
Connect via Zapier: Leads by Alex webhook → FUB Create Person action.
Create contacts or deals in HubSpot from every new lead.
Any HTTPS endpoint works. See the code examples above for signature verification.
Zapier quick-start
// In Zapier: use a "Webhooks by Zapier" trigger (Catch Hook)
// 1. Create a new Zap → Trigger: Webhooks by Zapier → Event: Catch Hook
// 2. Copy the webhook URL Zapier gives you
// 3. Paste it into Leads by Alex → Automations → Add endpoint
// 4. Click "Test" to send a sample payload
// 5. Zapier will detect the fields automatically
//
// The payload's "data" object fields will be available as variables
// in all subsequent Zapier steps.Best practices
Respond quickly
Return HTTP 200 within 10 seconds. Leads by Alex will retry once after 2 seconds on failure. If your processing takes longer, queue the payload and process it asynchronously.
Verify signatures
Always verify the X-Webhook-Signature before processing. Reject requests with an invalid or missing signature.
Guard against replays
Reject payloads where |now - timestamp| > 300 000 ms (5 minutes). Store processed event timestamps or IDs to deduplicate retries.
Use HTTPS endpoints only
HTTP URLs are rejected at save time. Use a valid TLS certificate — self-signed certificates are not accepted.
Test before going live
Each endpoint has a "Test" button in the Automations dashboard that sends a synthetic lead.created payload. Confirm delivery before relying on live data.
Keep your secret safe
Treat your webhook secret like a password. Rotate it by editing the endpoint. Never log or expose it in client-side code.
Ready to connect?
Add your first endpoint in the Automations dashboard. Use the Test button to verify delivery instantly.
Configure endpoints →