Unadhi Guides

Vendor privacy contact sheet: each supplier's DPO, privacy or grievance inbox from its own website, with the page it was found on and a re-check date

Updated 27 Oct 2026 · Disclosure: the website contact scraper linked below is an Unadhi product on the Apify Store. This guide is free, and the sheet also works if you copy contacts from each vendor's privacy policy by hand. Current Apify pricing is only on the Store page. This page has no affiliate links and nobody paid to be mentioned. This is not legal advice.

What you get: one Google Sheet with one row per vendor that handles personal data for your business (payroll, email marketing, CRM, hosting, an AI chatbot, a booking tool). Each row shows the vendor's best privacy contact taken from its own website (a DPO inbox first, then a privacy or data-request inbox, then a grievance inbox, then legal or compliance, then a general inbox), the page it was found on, its security inbox if listed, a Status column (OK / Weak / Missing / Skipped) and a Re-check on date. You use it the day a customer asks what a vendor holds about them, or a vendor tells you about a breach and you need to write back to the right place. Formulas are below. The example rows are made up.

The finished sheet

privacy tab (example rows are made up)

DomainStatusBest privacy contactTypeFound onSecurity inboxLast checkedRe-check onNext step
payrollco.exampleokdpo@payrollco.exampleDPOpayrollco.example/privacysecurity@payrollco.example27 Oct 202627 Apr 2027OK: DPO inbox on file
mailblast.exampleokprivacy@mailblast.examplePrivacymailblast.example/legal/privacy27 Oct 202627 Apr 2027OK: privacy inbox on file
chatbotly.exampleoklegal@chatbotly.exampleLegalchatbotly.example/terms27 Oct 202627 Apr 2027Weak: legal inbox only, read its privacy policy
smallhost.exampleokinfo@smallhost.exampleGeneralsmallhost.example/contact27 Oct 202627 Apr 2027Weak: general inbox only, ask for a privacy contact
bigcloud.exampleblocked-by-termsSkipped: copy the contact from its privacy policy by hand

The six-month re-check is our working rule, not a legal deadline. Change it in one cell if you prefer every three or twelve months.

Why keep vendor privacy contacts in one place

The sheet sits next to your record of processing. If you are building that too, see the Article 30 records of processing sheet and the Article 28 processor agreement check sheet.

Step 1: list your vendors in a tab named vendors

ColumnWhat to put
A VendorName, for example "PayrollCo"
B WebsiteThe vendor's homepage, for example https://www.payrollco.example
C What it does for youPayroll, newsletter, CRM, website hosting, AI chatbot, appointment booking...
D Personal data it getsFor example "staff names, bank details" or "customer emails, chat transcripts"
E RoleProcessor (acts on your instructions), Separate controller or Not sure
F CountryWhere the vendor is based

The quickest way to find vendors is your last 12 months of card and bank statements plus the "connected apps" page of your email and website tools.

Step 2: run the contact scraper on the vendor websites

Open the website contact scraper on the Apify Store and paste column B into urls (one per line). What it does, as written in the Actor's input schema and code on 27 October 2026:

When the run finishes, export the dataset as CSV, then in your sheet use File → Import → Upload, "Insert new sheet", and rename it scrape. Nested fields become headers like emails/0/email and emails/0/foundOn, so the formulas below find columns by header name, not by position.

Free route

For ten vendors, open each privacy policy, search the page for "@", and type what you find into a scrape tab with the headers domain, status (type ok), emails/0/email, emails/0/foundOn, emails/1/email, emails/1/foundOn and so on. The formulas work the same.

Step 3: build the privacy tab

Create a tab named privacy, put these headers in row 1 and these formulas in row 2, and fill down as many rows as scrape has.

ColHeaderFormula (row 2)
ADomain=IFERROR(INDEX(scrape!$A2:$ZZ2, MATCH("domain", scrape!$A$1:$ZZ$1, 0)), "")
BStatus=IFERROR(INDEX(scrape!$A2:$ZZ2, MATCH("status", scrape!$A$1:$ZZ$1, 0)), "")
CAll inboxes=IFERROR(TEXTJOIN(", ", TRUE, FILTER(scrape!$A2:$ZZ2, REGEXMATCH(scrape!$A$1:$ZZ$1, "^emails/\d+/email$"))), "")
DDPO inbox=IFERROR(REGEXEXTRACT(", "&C2, ", (dpo(?:[.\-_][a-z0-9]+)?@[^,]+)"), "")
EPrivacy inbox=IFERROR(REGEXEXTRACT(", "&C2, ", ((?:privacy|dsar|dsarequests)(?:[.\-_][a-z0-9]+)?@[^,]+)"), "")
FGrievance inbox=IFERROR(REGEXEXTRACT(", "&C2, ", ((?:grievance|grievances|nodal)(?:[.\-_][a-z0-9]+)?@[^,]+)"), "")
GLegal inbox=IFERROR(REGEXEXTRACT(", "&C2, ", ((?:legal|compliance)(?:[.\-_][a-z0-9]+)?@[^,]+)"), "")
HGeneral inbox=IFERROR(REGEXEXTRACT(", "&C2, ", ((?:support|help|info|contact|hello)(?:[.\-_][a-z0-9]+)?@[^,]+)"), "")
IBest privacy contact=IF(D2<>"", D2, IF(E2<>"", E2, IF(F2<>"", F2, IF(G2<>"", G2, H2))))
JType=IFS(D2<>"", "DPO", E2<>"", "Privacy", F2<>"", "Grievance", G2<>"", "Legal", H2<>"", "General", TRUE, "")
KFound on=IFERROR(INDEX(scrape!$A2:$ZZ2, MATCH(SUBSTITUTE(INDEX(scrape!$A$1:$ZZ$1, MATCH(I2, scrape!$A2:$ZZ2, 0)), "/email", "/foundOn"), scrape!$A$1:$ZZ$1, 0)), "")
LSecurity inbox=IFERROR(REGEXEXTRACT(", "&C2, ", (security(?:[.\-_][a-z0-9]+)?@[^,]+)"), "")
MNamed inboxes dropped=IFERROR(INDEX(scrape!$A2:$ZZ2, MATCH("personalEmailsDropped", scrape!$A$1:$ZZ$1, 0)), 0)
NLast checkedType the date you confirmed the contact (from the run date or by opening the page)
ORe-check on=IF(N2="", "", EDATE(N2, 6))
PNext step=IFS(B2<>"ok", "Skipped ("&B2&"): copy the contact from its privacy policy by hand", AND(O2<>"", TODAY()>=O2), "Re-check now", J2="DPO", "OK: DPO inbox on file", J2="Privacy", "OK: privacy inbox on file", J2="Grievance", "OK: grievance inbox on file", AND(J2="Legal", M2>0), "Weak: legal inbox only, and a named or dataprotection@ inbox was dropped: read its privacy policy", J2="Legal", "Weak: legal inbox only, read its privacy policy", J2="General", "Weak: general inbox only, ask for a privacy contact", TRUE, "Missing: ask your account manager in writing")

Notes on the formulas:

Step 4: fix the Weak and Missing rows

Step 5: use it

Add a tab named plan. In A1: =QUERY(privacy!A2:P, "select P, count(A) where A is not null group by P order by count(A) desc label P 'Next step', count(A) 'Vendors'", 0). Work the Skipped, Missing and Weak rows first. Put a calendar reminder on the earliest Re-check on date.

Keep the sheet with the people who answer customer data requests. It holds only shared company inboxes, not anyone's personal address.

Partner programme opening soon

Partner programme opening soon: join the free waitlist. Planned terms are 50% of each referred sale (75% once you have 5+ paying customers), single level, paid through Gumroad only from revenue received.

Sources

Related

← All guides

Unadhi Guides are free, independent explainers. We correct errors as soon as we find them: write to mininglife7@gmail.com.