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_1312323123131231". Set PEOPLE_DATA_LABS_WEBHOOK_HEADER to the name (default Authorization) and PEOPLE_DATA_LABS_WEBHOOK_SECRET to the exact, whole value, including any Bearer prefix 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"]
EventSent When
person record update batchA 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 the mobile_phone and phone_numbers fields), 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. Return 200 only 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


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 9, 2026

View on GitHub →