Agent skill
People Data Labs Webhooks Skill
Receive and authenticate People Data Labs (PDL) webhooks. Use when setting up a PDL webhook endpoint, checking the shared secret header, debugging 401s, or handling the monthly batches of updated person record IDs sent after each data release.
Install this skill
npx skills add hookdeck/webhook-skills --skill people-data-labs-webhooks
People Data Labs (PDL) sends a webhook when a data release updates person records that match the filters registered for your account (for example job_change, work_email or location). The body is a JSON array of PDL IDs, sent in batches of up to 1,000, roughly once a month. Webhooks are available to Enterprise API and Data License customers only.
PDL webhooks are not signed. There is no signature header and no timestamp. You authenticate a request with a secret header that you ask PDL to include when they register the webhook.
The People Data Labs team registers webhooks for Enterprise customers. Ask them to include a secret header.
When to Use This Skill
- How do I receive People Data Labs webhooks?
- How do I authenticate PDL webhook requests? (Compare the shared secret header)
- Does PDL sign webhook payloads? (No)
- How do I handle the batches of updated person IDs PDL sends after a data release?
- Why does my PDL webhook endpoint return 401?
Verification (core)
const crypto = require('crypto');
// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
const ha = crypto.createHash('sha256').update(String(a)).digest();
const hb = crypto.createHash('sha256').update(String(b)).digest();
return crypto.timingSafeEqual(ha, hb);
}
function verifyPdlRequest(headers, {
name = (process.env.PEOPLE_DATA_LABS_WEBHOOK_HEADER || 'Authorization').toLowerCase(),
secret = process.env.PEOPLE_DATA_LABS_WEBHOOK_SECRET,
} = {}) {
if (!secret) return false; // never accept when unconfigured
const received = headers[name.toLowerCase()]; // Node lowercases header names
return Boolean(received) && safeEqual(received, secret); // compare the WHOLE value
}
- The header name is whatever PDL was told to send. The docs' example is
"Authorization": "Bearer pk_. Set1312323123131231" PEOPLE_to the name (defaultDATA_ LABS_ WEBHOOK_ HEADER Authorization) andPEOPLE_to the exact, whole value, including anyDATA_ LABS_ WEBHOOK_ SECRET Bearerprefix you typed. Do not strip or parse the prefix. - Look the header up case-insensitively. A missing header or a mismatch returns
401. - Read the body raw (
express.raw({ type: '*/*' })) so any content type works, check the header first, and parse the JSON only after that. - A static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS only, and rotate the credential with the PDL team if it leaks.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Payload and Event Types
PDL has no event-type field. Every request is the same kind of event: a JSON array of PDL IDs for records updated in a release.
["9adnit66-hdZ5lkBbq-AOQ_0000", "YraxDoP16jzyjfxtn-1XJQ_0000", "6Wu6sKeyNfaLF3CVBgoysg_0000"]
| Event | Sent When |
|---|---|
| person record update batch | A data release updates records matching your registered filters (up to 1,000 IDs per request, about monthly) |
- Filters are chosen at registration and are not in the payload:
education,job_change,location,mobile_phone_number,personal_emails,phone_number(watches themobile_phoneandphone_numbersfields),social_profile,work_email. With several filters on one webhook, an ID can appear more than once, so dedupe. Consider one webhook per filter. - PDL does not retry. If you don't return
200, ask your PDL team to resend the whole batch. Return200only once the IDs are stored, and make processing idempotent.
Environment Variables
PEOPLE_DATA_LABS_WEBHOOK_HEADER=Authorization # header name PDL sends (default Authorization)
PEOPLE_DATA_LABS_WEBHOOK_SECRET=Bearer your-shared-key # exact, whole header value
Local Development
PDL can't reach localhost. Tunnel with the Hookdeck CLI (no account required):
npx hookdeck-cli listen 3000 people-data-labs --path /webhooks/people-data-labs
Give the printed URL to your PDL team as the target URL.
Resources
- references/overview.md - What PDL webhooks are, filters and payload shape
- references/setup.md - Getting a webhook registered with a secret header
- references/verification.md - Header check details, gotchas, debugging 401s
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests