• 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:

respondgrowth
PurposeAnswer exactly what was askedAlso follow up, nurture, offer
ExamplesAn engineering framework site, a FOSS blogMy consulting brand site
Legal basisThe person’s request; consent for newslettersThe same, plus consent for marketing email
Form extrasNoneAn 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.

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_version says which wording they agreed to. When the wording changes, the version changes, and old records still point at what was shown at the time.
  • at records 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:

DataKeptEnforced by
Messages / questions24 months, then deleted automaticallyKV expiry set at write time
Ebook requests24 monthsKV expiry
SubscriptionsUntil unsubscribeNo expiry; unsubscribe marks them
Homelab backup copyMirrors the abovePurges 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.

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.