Docs · Deliver leads · Last updated October 31, 2026
Webhooks
Send leads to any URL as JSON: your CRM, a database, n8n, Make or your own code. Every batch can be signed so you know it came from us.
How it works
A webhook is an export destination, like Instantly or HubSpot. Choose Webhook when you export a segment of leads, or as a monitor's destination to receive each run's new businesses. We POST the leads in batches of 200. The segment's rules apply exactly as for a file: the email grades you chose (Verified only by default), your do-not-contact list, one row per business.
Payload
POST /your/endpoint HTTP/1.1
Content-Type: application/json
X-LeadGrid-Signature: sha256=5d1e0c…
{
"source": "leadgrid",
"export_id": 3141,
"count": 200,
"leads": [
{
"email": "hello@brightsmile.example",
"email_grade": "valid",
"business": "Bright Smile Dental",
"category": "Dentist",
"phone": "+15125550142",
"website": "https://brightsmile.example/",
"city": "Austin",
"state": "Texas",
"country": "US",
"in_area": true,
"place_id": 48213,
"google_place_id": "ChIJ…"
}
]
}Each item in leads is the same record our CSV exports use (the full field list is below). Delivering a Verified email uses a checked-email credit the first time, as with any export.
Signing and verification
In the app, open Settings → Integrations → Webhook and generate a signing secret. From then on every request carries X-LeadGrid-Signature: sha256=<hex>, an HMAC-SHA256 of the raw request body keyed with your secret. Recompute it and compare in constant time; reject anything that doesn't match. You can rotate or remove the secret at any time.
import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.LEADRAFT_WEBHOOK_SECRET;
// Verify against the raw body, before any JSON parsing.
app.post("/leadraft", express.raw({ type: "application/json" }), (req, res) => {
const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");
const given = req.get("X-LeadGrid-Signature") ?? "";
const ok = given.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
if (!ok) return res.status(401).end();
const { leads } = JSON.parse(req.body.toString("utf8"));
// Upsert on place_id: a retried batch can arrive twice.
res.status(200).end();
});Delivery and retries
- Answer with any
2xxwithin 30 seconds to accept a batch. - Network errors and
5xxanswers are retried up to 3 attempts in total, waiting 1 s then 2 s. A4xxanswer stops the push. - Redirects aren't followed: give us your endpoint's final URL.
- The URL must be a public
http(s)address; private and local networks are refused. - A batch can arrive more than once (a retried or resumed push), so make your handler idempotent: upsert on
place_id.
Lead fields
| Field | Description |
|---|---|
email | Best sendable address. |
email_grade | valid · catch_all · risky · unknown. |
email_confidence | 0–1. |
email_role | e.g. generic (info@), personal, sales. |
email_source | Where it was found (website page, sitemap, rendered page, pattern…). |
email_note | The mail server's answer, shortened. |
email_verified_at | ISO time of the check. |
free_provider | true for Gmail, Outlook and similar. |
email_2 … email_5, email_N_grade | Other sendable addresses, best first. |
| Business | |
business | Name. |
category, categories | Google Maps category and all categories. |
rating, reviews, price_level | From the listing. |
phone, phone_display, phone_type | E.164, as listed, and line type when known. |
website, website_platform, website_phones, contact_form | Site, detected platform, phones on the site, whether it has a contact form. |
hours | Opening hours for the week. |
facebook, instagram, linkedin, x, tiktok, youtube, yelp, whatsapp | Social profiles found on the site. |
| Location | |
address, city, state, postal_code, country | Address parts; country is a two-letter code. |
lat, lng | Coordinates. |
service_area | true for a service-area business (no storefront). |
in_area, distance_km | Inside the searched area, and km from its boundary (0 inside). |
| Identifiers | |
place_id | Leadraft's lead id (the `id` in the API). |
google_place_id, cid, maps_url, reviews_url | Google identifiers and links. |
first_seen, last_seen | When we first and last saw the listing. |