Configure the exposure before copying code
Create a named widget
Use one widget per site or risk boundary so usage and revocation stay attributable.Add exact HTTPS origins
Enter 1–20 complete origins such ashttps://www.example.com. Wildcards, paths, query strings, URL credentials,null, and HTTP are rejected.Set a daily hard credit cap
Choose 1–50,000 credits. Start low, observe legitimate volume, then raise deliberately.Choose blocking categories
The dashboard starts withundeliverableselected. With that default, risky and unknown results warn and allow submission. You may remove that block or explicitly add invalid syntax, disposable, spam-trap, or disabled results.Copy the public site key
A key starts withpdw_. It is designed to be visible in browser code and is not a secret.
Install the script and bind a real form
The script automatically binds only forms marked with data-peopledat.
<form action="/join" method="post" data-peopledat>
<label for="work-email">Work email</label>
<input id="work-email" name="email" type="email" required>
<button type="submit">Continue</button>
</form>
<script
src="https://peopledat.com/widget/v1.js"
data-site-key="pdw_REPLACE_WITH_YOUR_SITE_KEY"
defer>
</script>- Put
data-peopledaton each form you want automatically checked. - Use an
input[type=email]orinput[name=email]. - Keep your normal form action and server-side validation.
- Load the script with
deferso markup is available when it binds. - Use
data-site-keyon a form to override the script’s default key for that form. - The script adds subtle “Checked by PeopleDat” attribution when enabled.
- The base binding does not render positive or warning result UI. Add your own listener if you want visible feedback.
Build optional feedback from the result event
Every completed check emits a bubbling peopledat:result event before an allowed form is resubmitted. The markup and listener below are custom feedback; the base snippet does not render them for you.
<p id="email-feedback" role="status" aria-live="polite"></p>
<script>
const form = document.querySelector('[data-peopledat]')
const feedback = document.querySelector('#email-feedback')
form.addEventListener('peopledat:result', (event) => {
const { allow, status, warning } = event.detail
feedback.dataset.state = !allow
? 'blocked'
: status === 'deliverable'
? 'positive'
: warning
? 'warning'
: 'neutral'
feedback.textContent = !allow
? 'Please use a deliverable email address.'
: status === 'deliverable'
? 'Looks deliverable.'
: warning
? 'We could not confirm this address, but you can continue.'
: ''
})
</script>| Status | Default | Meaning |
|---|---|---|
| deliverable | Allow | The Quick result supports sending. |
| review | Warn/allow | A risky category needs judgment; blocks only when explicitly selected. |
| invalid | Block | An undeliverable result. New dashboard widgets block it by default; removing that selection makes it advisory. |
| unavailable | Always allow | Timeout, outage, unknown, exclusion, credit/cap issue, or another inconclusive state. |
With the dashboard default, risky results emit review with allow: true and warning: true; unknown results emit unavailable with the same allow/warning flags. A default-blocked undeliverable result emits invalid with allow: false.
The browser receives coarse reasons by design. It never receives provider internals, cache details, raw API credentials, or other workspace data.
Use the small JavaScript API for custom forms
The loaded script exposes an immutable window.PeopleDat object with version, verify, and bind.
const form = document.querySelector('#newsletter-form')
window.PeopleDat.bind(form)const result = await window.PeopleDat.verify({
siteKey: 'pdw_REPLACE_WITH_YOUR_SITE_KEY',
email: '[email protected]',
requestId: crypto.randomUUID()
})
if (result.status === 'unavailable') {
// Fail open: continue your normal workflow.
}Use a unique request ID for each visitor action. The automatic form binding creates one for you and prevents duplicate pending checks or resubmission loops.
Keep browser and server responsibilities separate
- Treat the site key as public; never put a
pd_live_…API key in browser code. - Keep normal server-side validation, abuse controls, consent checks, and authorization.
- Use the widget only as a point-in-time email-quality signal.
- Add
https://peopledat.comto bothscript-srcandconnect-srcin a strict Content Security Policy. - Do not send the result as proof of identity or mailbox ownership.
- Monitor usage and pause/revoke a widget that shows unexpected volume.
Content-Security-Policy:
script-src 'self' https://peopledat.com;
connect-src 'self' https://peopledat.com;Know exactly when the widget fails open
Timeouts, PeopleDat outages, provider failures, unknown/excluded results, exhausted credits, daily-cap denials, and other inconclusive states return an allowed unavailable result. The SDK also catches network, CORS, parsing, and abort errors and returns an allowed response.
PeopleDat uses a shorter server deadline than the browser timeout. If work completes too late to influence the visitor, the interaction is reconciled to zero billed credits.
Troubleshoot the widget
Nothing happens on submit
Confirm the form has data-peopledat, an email input, and the deferred script loaded without a CSP error. The script tag alone does not bind an unmarked form.
Every result is unavailable
Check that the exact page origin—including www/non-www and port—is active, the widget/entitlement is active, and connect-src permits peopledat.com.
The form submits twice or loops
Do not add a second submit handler that resubmits independently. Let PeopleDat.bind own its one approved resubmission.
A risky result is not blocked
That matches the dashboard default: only undeliverable starts selected. Risky results warn and allow unless you explicitly add a matching category; unknown and unavailable always warn and allow.
Unexpected credit use
Lower the hard daily cap, inspect origin/usage attribution, rotate by archiving the widget, and create a replacement site key.