Validate emails in Pipedrive
Check the email address of each Pipedrive person and save the result in custom person fields that you pick, so your filters and automations can use it. Two small pieces do the work, and you can use either or both:
- A webhook relay checks a person within seconds when it is added, or when its primary address changes.
- A clean-up command checks every person of a Pipedrive filter that has no result yet.
Both run in your own accounts with your own keys: the relay as a Cloudflare Worker, the command with Node. Nothing needs to be installed from the Pipedrive Marketplace.
Before you start
- An API key. Create one on the API keys page with the permission "Validate addresses". To clean a filter of more than 50 persons, also turn on "Bulk lists". A daily credit cap limits what a busy webhook can spend.
- Your Pipedrive API token. In Pipedrive: Settings → Personal preferences → API. The token acts as you, so the relay sees only the persons you can see. Keep it secret.
- Your company domain, the address you open Pipedrive at:
acmeforhttps://acme.pipedrive.com. - Node 22.18 or later for the clean-up command, and a Cloudflare account for the relay.
- The files, which are built from our tested code:
- relay.js: the webhook relay, one Cloudflare Worker module;
- clean.mjs: the clean-up command and the field setup, one Node module;
- webhook.json: the webhook's settings, for Pipedrive's API.
Create the result fields
The result goes into three custom person fields:
| Field | Value |
|---|---|
| Email status | one of the five result statuses: valid, invalid, catch_all, unknown or disposable |
| Email status reason | why, such as accepted or no_mailbox (optional) |
| Email checked at | when the check ran, as an ISO 8601 date and time in UTC |
Create them once with the command:
export PIPEDRIVE_API_TOKEN=… PIPEDRIVE_DOMAIN=acme
node clean.mjs --setupIt creates whichever of the three fields are missing, as Text fields, and prints their keys. To use fields of your own instead, set PIPEDRIVE_STATUS_FIELD, PIPEDRIVE_CHECKED_AT_FIELD and, if you want it, PIPEDRIVE_REASON_FIELD to each field's name or key, for both pieces. They must be custom person fields of type Text or Large text.
Check new and changed persons
Deploy the relay to your Cloudflare account and give it its secrets:
npx wrangler deploy relay.js --name ev-pipedrive-relay --compatibility-date 2026-10-01 --var PIPEDRIVE_DOMAIN:acme npx wrangler secret put EV_API_KEY --name ev-pipedrive-relay npx wrangler secret put PIPEDRIVE_API_TOKEN --name ev-pipedrive-relay npx wrangler secret put WEBHOOK_USER --name ev-pipedrive-relay npx wrangler secret put WEBHOOK_PASSWORD --name ev-pipedrive-relayWEBHOOK_USERis any name, such aspipedrive.WEBHOOK_PASSWORDneeds at least 32 random letters and digits, for example fromopenssl rand -hex 32. Your keys stay in the Worker's secret store. For EU data residency, add--var EV_REGION:euto the deploy command.In Pipedrive, open Settings → Tools and apps → Webhooks → Create new webhook:
- Event action
*(or one webhook forcreateand one forchange), event objectperson; - Endpoint URL the relay's address, such as
https://ev-pipedrive-relay.<your-subdomain>.workers.dev/; - HTTP Auth the
WEBHOOK_USERandWEBHOOK_PASSWORDyou set on the relay.
To create the webhook with Pipedrive's API instead, send
webhook.json, with your values filled in, toPOST /api/v1/webhooks.- Event action
Add a test person with the address
no_mailbox@sandbox.email-validation.org, a test address that costs nothing. Within a few seconds its Email status isinvalid. Then delete the test person.
The relay checks a person when it is created, and when its primary address changes. It ignores every other change, including its own write to the result fields, so a result never starts a second check. Before it checks, it reads the person, so it always checks the address as it is now. A repeated delivery of an event it already checked is skipped, and the relay refuses any request without the webhook's credentials.
Clean a filter
Create a person filter in Pipedrive with the persons to check. Its id is the number at the end of the address while the filter is open, such as 12 in …/persons/list/filter/12.
export EV_API_KEY=… PIPEDRIVE_API_TOKEN=… PIPEDRIVE_DOMAIN=acme
node clean.mjs --filter 12 --dry-run # counts what it would check
node clean.mjs --filter 12 # checks and writes the resultsThe command skips persons without an address and persons that already have a status other than unknown (add --revalidate to check them again). Up to 50 persons are checked one by one. More go into one bulk job: the command uploads the list, starts it, waits for it, and writes each result to its person.
If a run stops for any reason, such as no credits or a closed laptop, run the same command again. It keeps the bulk job's id in a small state file (ids only, no addresses) and resumes that job, so a list is never paid for twice. Exit code 75 means the job was still running when the command stopped waiting: run it again later.
Other options: --region eu for EU data residency, --status-field, --reason-field and --checked-at-field for your own fields, --rate for Pipedrive requests per second (default 8), and --wait-minutes for how long to wait for a bulk job (default 240).
Answers and retries
Pipedrive treats any answer other than 2xx as a failure: it delivers again up to three times, and it can pause or remove a webhook that keeps failing. So the relay answers with an error only when trying again can work.
| The relay answers | When | What to do |
|---|---|---|
200 saved | The person was checked and the result written | Nothing |
200 ignored or skipped | No new address, the person is gone, or the event was already checked | Nothing |
200 not_checked | The key was refused, there are no credits, the key reached its daily cap, or a result field is missing | Fix the cause, then clean the filter to catch up |
200 not_saved | The check ran but Pipedrive did not take the result | Clean the filter to catch up |
503 | A rate limit or an outage before the check | Nothing: Pipedrive delivers again |
401 | Wrong or missing webhook credentials | Check HTTP Auth on the webhook |
Nothing is charged for a delivery that answers 503 or not_checked.
Costs and limits
- Each checked person is one check. Results with the status
unknownare free, and so are the test addresses. - The relay answers within Pipedrive's 10-second limit, so it gives a check at most 5 seconds.
- Each read and write uses Pipedrive API tokens from your company's daily budget. The clean-up command spaces its requests to stay under Pipedrive's rate limits and waits when Pipedrive asks it to.
- Our own rate limits apply to the relay as to any other client.