- Aug 17, 2026
- 6 min read
GDPR-Friendly Lead Capture for a Small EU Business
Not legal advice. This is what I implemented for my own sites as a sole trader in Poland, and why. I’m an engineer, not a lawyer. Rules differ by country and situation; have a lawyer review anything you rely on, especially before marketing campaigns.
Lead capture is easy to build and easy to get wrong. A form, a database and a “subscribe” checkbox can collect personal data on three different legal bases without anyone noticing. This post closes the series on my lead pipeline with the privacy side: the rules I set, how each is enforced in code, and the gaps I accept knowingly.
Two kinds of site: respond and growth
The most useful decision was to classify every site by what it may do with a lead:
| respond | growth | |
|---|---|---|
| Purpose | Answer exactly what was asked | Also follow up, nurture, offer |
| Examples | An engineering framework site, a FOSS blog | My consulting brand site |
| Legal basis | The person’s request; consent for newsletters | The same, plus consent for marketing email |
| Form extras | None | An optional, unticked “send me occasional updates” box |
| Privacy text | ”Used only to answer your request” | Also describes updates and how to withdraw |
The profile lives in the site registry of my forms Worker. A respond site simply ignores a marketing field, so a copy-paste mistake in a form can’t create a consent that the privacy page doesn’t cover. There’s a test for exactly that.
Consent you can prove
When someone ticks a box, the Worker stores more than true:
"consent": { "newsletter": true, "text_version": "2026-10-04", "at": "2026-10-04T12:00:00.000Z" }
text_versionsays which wording they agreed to. When the wording changes, the version changes, and old records still point at what was shown at the time.atrecords when.- The box is never pre-ticked, and a newsletter form without the box
checked is rejected with
consent_required.
Double opt-in
A ticked box proves that someone typed an address. Double opt-in proves the
address owner wants the emails. Subscriptions are stored as pending; a
confirmation email arrives within seconds; only confirmed addresses ever
receive a newsletter or a drip.
Two details matter for privacy as much as for deliverability:
- The confirm link opens a page with a button; only the button confirms. Corporate mail scanners open every link, and a GET that confirms would subscribe people automatically.
- Every marketing email carries a visible unsubscribe link plus one-click unsubscribe headers.
The implementation is in double opt-in with signed links.
Retention per kind
Different data, different lifetimes, written into the privacy page and enforced by the platform:
| Data | Kept | Enforced by |
|---|---|---|
| Messages / questions | 24 months, then deleted automatically | KV expiry set at write time |
| Ebook requests | 24 months | KV expiry |
| Subscriptions | Until unsubscribe | No expiry; unsubscribe marks them |
| Homelab backup copy | Mirrors the above | Purges what expired in KV, re-checks retention |
The backup point is easy to miss. A local copy that keeps leads after the source deleted them quietly breaks the policy. More in retention as a KV TTL and the pull-only homelab mirror.
Alerts without personal data
I want to know immediately when a lead arrives, so the Worker sends a push
notification. The alert contains the site, the form kind, the project chosen,
the country and only the email domain: from @acme.com, never the name,
address or message. Phones show notifications on lock screens; the full lead
stays in storage.
Unsubscribe without keeping the address
An unsubscribe must be remembered forever, otherwise the next import or form submission emails the person again. But keeping the address “to remember not to email it” is itself personal data.
The do-not-send list stores sha256(lowercased email) with a date, the site
and the source (link or one-click). Every sender hashes the address it’s
about to email and checks the list. Details in
a suppression list without storing addresses.
The right to erasure, as a command
“Delete my data” requests should take one command, not an afternoon:
leadshub forget [email protected] --dry-run # what would go
leadshub forget [email protected] --kv # local copy + cloud store
It removes the person’s leads, drip enrolments and send history. It keeps the suppression hash on purpose: that hash is what stops them being emailed again. Backups hold a copy until they rotate out (14 days), and the privacy wording should say so.
Polish e-communications law: consent first, B2B included
GDPR is only half of it. Poland’s electronic communications law (PKE, in force since late 2024) requires prior consent for unsolicited commercial email, and the B2B exemption people assume doesn’t exist there. In practice:
- A person who asked a question has not agreed to marketing. Answering them is fine; adding them to a sequence isn’t.
- Cold email to sourced contacts is risky without consent, whatever GDPR’s legitimate interest allows.
- That’s why the growth profile adds an explicit, optional box rather than inferring interest from a contact form.
What I keep off the site, and the gap I accept
Polish law on electronic services expects a provider’s business address and tax identifiers to be shown. As a sole trader, mine would be my home address, and showing the tax number alone doesn’t help: anyone can look up the address from it in the public business register.
My compromise, written down as a known gap: no address or tax numbers on the sites while there’s no billing; a contact email and the complaint authority instead. Before the first invoice: rent a virtual office, change the correspondence address in the register, then publish both.
The privacy page names the processors
Visitors should be able to see who touches their data. My page lists:
- Cloudflare, for hosting, the forms Worker, storage and web analytics (with the transfer basis for processing outside the EU);
- the email provider (Mailtrap in my setup), added before the first production email goes out;
- the complaint authority, Poland’s data protection office (UODO).
The homelab and the self-hosted notification server are mine, so they aren’t third-party processors, but the page still says what’s stored and for how long.
Takeaways
- Decide per site what a lead may be used for (respond vs growth) and make the code enforce it.
- Store consent with wording version and time, never pre-tick, and use double opt-in for anything you’ll email.
- Put retention in the platform and make backups honour it.
- Alerts carry no personal details; suppression lists keep hashes; erasure is one command.
- In Poland, commercial email needs prior consent, B2B too.
- Write down the gaps you accept, and when you’ll close them.