Skip to main content

Marketing and email

Marketing email and promotional push have their own consent rules, their own unsubscribe rules, and their own ways to break sign-in. Read this before you send any email that is not strictly about the user's account.

How it works​

Two streams, never mixed:

  • Transactional messages are part of the service the user asked for. One-time codes, password resets, receipts, security alerts, "we changed our privacy policy". They need no marketing consent, and they must never be blocked by a marketing unsubscribe.
  • Marketing messages promote something. They need a lawful basis, an unsubscribe in every message, and a check against the suppression list before every send.

A suppression list is the list of addresses we must not market to: unsubscribes, hard bounces, spam complaints and manual do-not-contact entries.

How to build it​

1. Decide opt-in or opt-out per product and country​

This is a legal decision. Write it down per product and per market before the first send.

SituationUsual starting positionWhy
EU/UK users, marketing emailOpt-in, unticked box, never bundled with the termsGDPR consent rules plus the ePrivacy rules on electronic marketing
EU/UK existing customer, similar productsOpt-out may be allowed ("soft opt-in")National ePrivacy rules allow it in narrow cases. Ask legal for each country
India usersOpt-in by clear affirmative action, with an itemised noticeDPDP Act has no legitimate-interests basis. See DPDP
US usersOpt-out is lawful under CAN-SPAM, but opt-in is safer for deliverabilityMailbox providers punish complaints regardless of law
Known under 18Do not marketChildren's rules in DPDP and GDPR. See Children's data
Promotional push notificationsExplicit in-app opt-inApple App Review Guideline 4.5.4 requires it, with an opt-out

Store each choice as a consent purpose (marketing_email, marketing_push, or a product-scoped purpose like example-app_updates), as on The consent model. If a product legitimately uses an opt-out basis, model it as a purpose with default_state = granted and document the basis in the record of processing.

2. Separate the sending infrastructure​

  • Send marketing from a different sender domain (for example news.example.com) and, ideally, a different provider or account than one-time codes.
  • Publish SPF, DKIM and DMARC for every sending domain before any bulk send.
  • Watch complaint rate in the mailbox provider's tools. Google's guidelines for senders of more than 5,000 messages a day to Gmail ask for a reported spam rate below 0.30% (Google email sender guidelines, as of October 2026).

3. Every marketing message carries​

  1. A visible unsubscribe link that works without logging in.
  2. A List-Unsubscribe header and List-Unsubscribe-Post: List-Unsubscribe=One-Click (RFC 8058). A bare POST to the URL must unsubscribe at once, with no confirmation page.
  3. The sender's legal name and postal address, and a one-line reason ("you get this because you opted in to product news").
  4. Links to the privacy policy and terms.

Honour unsubscribes fast. Google's sender FAQ treats requests not honoured within 48 hours as non-compliant (Email sender guidelines FAQ, as of October 2026). Our target is immediate.

4. The unsubscribe token​

token = base64url(payload) + "." + base64url(HMAC_SHA256(secret, payload))
payload = { subject: <user or lead id>, purpose: "marketing_email",
campaign: <campaign id>, issued: <unix time> }
  • Opaque and signed. Never put an email address or a readable user id in the URL.
  • Include the campaign id, and record it on the denied consent event. Then you can tell which campaign drove unsubscribes, and prove which message the person was answering.
  • Long-lived. A link in an old email must still work.
  • Invalid and unknown tokens return the same generic response, so the endpoint cannot be used to test whether an address is subscribed.
  • Idempotent. A second click is a success.

5. Suppression and the send job​

  • Build the audience query to require current consent for the purpose and to exclude the suppression list, deleted accounts and accounts with an open deletion request.
  • Run sends from the product's own tool, which logs who sent what to which segment and how many. Not from a one-off script.
  • Store suppression entries as a hash of the normalised address when you can, so the list does its job without holding readable addresses.
  • Marketing suppression never suppresses transactional mail.

6. Ad audiences are marketing too​

Uploading customer emails (even hashed) to an ad platform for custom or lookalike audiences is sharing personal data for advertising. It needs its own purpose (for example ad_audience), a line in the privacy policy, store privacy-label entries, and a record of which users were in which upload, so a deletion request can remove them later.

Rules​

Rule

Never send marketing through the account that sends one-time sign-in codes. A spam complaint can put the address on an account-wide suppression list, and the next sign-in code is silently dropped. The user is locked out and the ticket looks like an auth bug.

Rule

Never bundle marketing consent into the "I agree to the terms" tick. Processing the user asked for and marketing they did not ask for are separate choices. A bundled tick is not freely given consent under GDPR or DPDP.

Rule

Check consent and the suppression list at send time, in code, for every send. A list exported last week is already wrong.

Mistakes we made​

  • One opt-in purpose for every product reached nobody. A single email_marketing purpose was shared across all products and had almost no grants, so an opt-in-only send would reach no one. The team used a product-specific purpose with a different default instead. Decide the basis per product before launch, not on the day of the campaign.
  • Unsubscribe links without a campaign id. The token carried user and purpose only. The record says "unsubscribed at 10:02" but not from which email. Put the campaign id in the token and the event.
  • A bulk send by script. A campaign went out from a one-off script straight to the email provider. The script skipped a lead's own marketing flag and wrote no send log. Build the send into the admin tool, where the consent filter and the audit row cannot be skipped.
  • Emails uploaded to an ad platform by hand. Sign-up emails were uploaded as a CSV for custom audiences, with no consent purpose, no policy disclosure and no record. The policy said the opposite. Pause such uploads until consent, disclosure and an export log exist.
  • An email account approved as "transactional only". The provider granted production sending on a declaration that no marketing would be sent. Sending campaigns from it contradicts that declaration. Use a separate account declared honestly as marketing.
  • No DMARC record on the main domain. Bulk mail without DMARC is filtered or rejected, and nobody could see spoofing of the domain. Add DMARC at p=none, read reports, then tighten in stages.

Checklist​

  • The message is classified as transactional or marketing, in writing.
  • The lawful basis per product and country is decided and recorded.
  • Consent purpose exists; audience query requires it at send time.
  • Known under-18 users are excluded.
  • Marketing uses its own sender domain; SPF, DKIM and DMARC pass.
  • Unsubscribe link, one-click header, postal address and reason are in the template.
  • Unsubscribe token is signed, opaque, carries the campaign id, and never expires.
  • The send is logged: who, when, segment, count.
  • Privacy policy and store labels mention marketing use of contact details.