Webhooks
When an export finishes, Listful can POST rows to destinations you configure. Sign in, add a destination, choose batch or one row, test.
Setup
Sign in, open /settings, add a destination (HTTPS URL), choose a mode and surfaces, then send a test. You can pause a destination without deleting it. Up to eight destinations per account.
Modes
Batch — one JSON body with a rows array (chunked under 1 MB). Use for n8n, Make, or your own server.
One row — one POST per person with flat fields. Use for Clay, where each webhook submission becomes one table row.
Request
We send POST with Content-Type: application/json. Respond 2xx to acknowledge. Delivery is queued when the export finishes, then retried on a schedule if your endpoint fails.
Retries
Transient failures (network, 408, 429, 5xx) retry on the schedule above. Permanent failures (other 4xx, redirects) stop immediately. Listful never emails on webhook failure. Make handlers idempotent on Idempotency-Key (jobId:chunkIndex or jobId:rowIndex). Replay stops working once the stored payload has been purged after 7 days, and the delivery ends as payload_expired.
Batch payload
{
"event": "export.completed",
"jobId": "8c4f1b9a-0ed2-4e5d-b317-f5e9b08a4c11",
"surfaceType": "search-people",
"sourceUrl": "https://www.linkedin.com/search/results/people/?keywords=product%20design",
"rowCount": 2,
"stopReason": "done",
"partial": false,
"exportedAt": "2026-09-30T12:00:00.000Z",
"chunkIndex": 0,
"chunkCount": 1,
"rows": [
{
"profile_url": "https://www.linkedin.com/in/averyparker/",
"full_name": "Avery Parker",
"headline": "VP Sales @ Helix Robotics",
"location": "San Francisco, California, US"
}
]
}| Field | Type | Description |
|---|---|---|
event | string | export.completed for batch. Check this first. |
jobId | string | Export job id. Use with chunkIndex for idempotency. |
surfaceType | enum | search-people, sales-search-people, post-reactions, post-comments, connections, group-members, event-attendees, company-employees. |
sourceUrl | string | The LinkedIn URL the user clicked Export on. |
rowCount | number | Total rows in the export (all chunks). |
stopReason | string | done, cancelled, cap_daily, and related stop codes. |
partial | boolean | True when stopReason is not done. |
exportedAt | string | ISO timestamp when the payload was stored. |
chunkIndex | number | 0-based chunk index. |
chunkCount | number | Total chunks for this destination. |
rows | object[] | Columnar rows matching the CSV for that surface. |
Row payload
{
"event": "export.row",
"jobId": "8c4f1b9a-0ed2-4e5d-b317-f5e9b08a4c11",
"surfaceType": "search-people",
"sourceUrl": "https://www.linkedin.com/search/results/people/?keywords=product%20design",
"rowCount": 240,
"rowIndex": 0,
"stopReason": "done",
"partial": false,
"exportedAt": "2026-09-30T12:00:00.000Z",
"profile_url": "https://www.linkedin.com/in/averyparker/",
"full_name": "Avery Parker",
"headline": "VP Sales @ Helix Robotics",
"location": "San Francisco, California, US"
}| Field | Type | Description |
|---|---|---|
event | string | Always export.row. |
rowIndex | number | 0-based index in the export. |
profile_url / full_name / … | string | CSV columns for that surface, flattened onto the object. |
Full header list by surface: CSV columns.
Signing
Every request includes X-Listful-Signature: t={timestamp},v1={hmac}— HMAC-SHA256 of timestamp + "." + rawBody with your secret. Compare with a timing-safe equal. Use the raw body.
// Node.js
const crypto = require('crypto');
const header = req.headers['x-listful-signature']; // t=…,v1=…
const parts = Object.fromEntries(
header.split(',').map((p) => p.trim().split('='))
);
const expected = crypto
.createHmac('sha256', process.env.LISTFUL_SECRET)
.update(`${parts.t}.${rawBody}`)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected))) {
return res.status(401).end();
}Examples
Webhook node → Split Out on rows[]. Verify X-Listful-Signature in a Code node when you want it. Use Batch mode.
Custom webhook → Iterator on rows[]. Bodies stay under 1 MB per chunk (Make caps at 5 MB). Use Batch mode.
Monitor webhook → One row mode. Each person is one POST and one Clay row. Map profile_url as the unique column. Optional auth header for Clay’s token. Clay limits submissions per webhook URL.
Express, FastAPI, Hono. Verify the signature, then insert. No SDK.
Need to set this up? Sign in, add a destination, send a test.
Configure webhook