# Webhooks: receive leads in your own system · Leadraft

URL: https://leadraft.co/docs/webhooks
Summary: Leadraft posts lead batches as JSON to your URL from exports and monitors, signed with HMAC-SHA256. Payload, retries and signature verification.

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

Request

```
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 `2xx` within 30 seconds to accept a batch.
-   Network errors and `5xx` answers are retried up to 3 attempts in total, waiting 1 s then 2 s. A `4xx` answer 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 |
| `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. |

## Related pages

-   [Live feedsPull instead of push](https://leadraft.co/docs/feeds)
-   [REST API reference](https://leadraft.co/docs/api)

Last built: 2026-11-09
