Nieuwsbrief: aanmelden, bevestigen, versturen via Resend¶
De winkel verzamelt aanmeldingen met double opt-in en zet de bevestigde adressen bij de contacten van Resend (Audience → Contacts). Versturen gebeurt in Resend zelf (Broadcasts). Alles staat achter een schakelaar in de admin die standaard uit staat.
Resend heeft in november 2025 de losse "audiences" vervangen door één contactenlijst per team, met segmenten (groepen) en topics (voorkeuren). Sinds 2026-10-19 gebruikt de code de nieuwe API zonder audience-ID.
Code: backend/apps/users/newsletter.py (aanmelden, bevestigen, afmelden, synchronisatie),
newsletter_views.py (endpoints), tasks.py (Celery), templates/email/newsletter_confirm.*
(bevestigingsmail), frontend/components/newsletter/NewsletterSignup.tsx (footer),
frontend/app/(shop)/checkout/page.tsx (vinkje). Tests:
apps/users/tests/test_nieuwsbrief.py.
Eenmalig inrichten (Ivar)¶
- Sleutel met Full access. De synchronisatie gebruikt
RESEND_API_KEY, dezelfde als voor de bestelmails, maar de contacten-API weigert een sleutel met alleen Sending access (401 "restricted to only send emails"; staging had zo'n sleutel tot 2026-10-19). Maak in Resend → API Keys een sleutel met Full access, zet die alsRESEND_API_KEYop staging en productie en herstart backend en Celery. Admin → Instellingen → Nieuwsbrief toont "Resend niet ingesteld" alleen als de sleutel ontbreekt; een sleutel met te weinig rechten zie je pas in de Celery-log ("Resend weigerde … 401"). - Segment (aanbevolen). Resend → Audience → Segments → Create segment, naam
"Nieuwsbrief". Open het segment en kopieer de ID uit de adresbalk of via het
code-icoon (een reeks als
78261eea-…). ZetRESEND_SEGMENT_ID=<die ID>in het env-bestand van staging en productie en herstart backend en Celery, zoals bij elke secret-wijziging. Elk nieuw bevestigd adres komt dan ook in dat segment, zodat een broadcast alleen naar de nieuwsbrief kan. Zonder segment komen de adressen alleen bij "alle contacten"; dat werkt ook, maar dan gaat een broadcast naar iedereen in Resend. - Controle. Meld een testadres aan, bevestig, en kijk in Resend → Audience → Contacts (en in het segment).
- Afzender. De bevestigingsmail komt van
DEFAULT_FROM_EMAILmet de gewone Reply-To, net als de bestelmails. Niets extra's nodig.
Aanzetten (Eefje of Ivar)¶
Admin → Instellingen → Nieuwsbrief → "Aanmelden voor de nieuwsbrief tonen" → Opslaan. Vanaf dan:
- de footer toont "Blijf op de hoogte" met een e-mailveld en de knop Aanmelden;
- de checkout toont onder het e-mailadres het vinkje "Houd me op de hoogte van nieuwe producten en acties", standaard uit. Aangevinkt meldt het adres aan bij het afrekenen.
Uitzetten haalt het veld en het vinkje weg. Bevestigen en afmelden via links uit eerdere mails blijven werken.
Wat de klant meemaakt¶
- Aanmelden via footer of checkout. De klant ziet "Bijna klaar: check je mail en bevestig je aanmelding." Ook een adres dat al bekend is krijgt dit antwoord, zodat niemand kan raden welke adressen bestaan.
- Bevestigingsmail "Bevestig je aanmelding" met de knop Ja, meld me aan. De link is 7 dagen geldig. Zonder klik gebeurt er niets; het adres staat dan als "wachtend" in de admin en komt niet in Resend.
- Na de klik komt de klant op de homepage met "Je aanmelding voor de nieuwsbrief is bevestigd." Het adres gaat direct naar de contacten van Resend.
- Afmelden via de link onderaan de bevestigingsmail of onderaan een nieuwsbrief. De klant ziet "Je bent afgemeld voor de nieuwsbrief." en staat in Resend op unsubscribed. Opnieuw aanmelden vraagt opnieuw om bevestiging.
Een verlopen bevestigingslink geeft "Deze bevestigingslink is verlopen. Meld je opnieuw aan."
Een nieuwsbrief versturen (Eefje)¶
- Resend → Broadcasts → Create broadcast.
- Kies bij de ontvangers het segment "Nieuwsbrief" (stap 2 van het inrichten), niet alle contacten.
- Schrijf de mail in de editor. Zet onderaan altijd een afmeldlink met de samenvoegcode
{{{RESEND_UNSUBSCRIBE_URL}}}; Resend vult die per ontvanger in en verwerkt het afmelden zelf bij het contact. (Onze eigen afmeldlink staat alleen in de bevestigingsmail.) - Stuur eerst een test naar jezelf (Send test), controleer de afmeldlink, en verstuur dan.
Richtlijn: af en toe (nieuwe producten, een actie, het seizoen), niet wekelijks. Elke mail die de klant niet verwacht, kost afmeldingen.
Cijfers¶
Admin → Instellingen → Nieuwsbrief toont: aangemeld (bevestigd en niet afgemeld), wachtend op bevestiging, afgemeld, en het aantal nieuwe bevestigingen in de laatste vier weken. Dezelfde cijfers staan in Resend onder Audience → Contacts; lopen ze uiteen, zie "Storingen".
Achter de schermen¶
| Wat | Waar | Wanneer |
|---|---|---|
| Aanmelding opslaan en bevestigingsmail | POST /api/newsletter/subscribe/ |
direct; 10 per uur per IP; 403 als de schakelaar uit staat |
| Bevestigen | GET /api/newsletter/bevestigen/<token>/ |
klik in de mail; token 7 dagen geldig |
| Afmelden | GET /api/newsletter/afmelden/<token>/ |
klik in de mail; token verloopt niet |
| Contact naar Resend zetten | Celery-taak newsletter.sync |
direct na bevestigen of afmelden |
| Inhaalslag | Celery Beat newsletter.sync_pending, elk uur op :40 |
alles wat bevestigd is en nog niet (opnieuw) gesynchroniseerd |
| Cijfers | GET /api/admin/newsletter/stats/ |
bij openen van de instellingen |
Resend-API: POST /contacts met {email, unsubscribed} en, als RESEND_SEGMENT_ID is
gezet, segments: [{id}]; bestaat het contact al, dan PATCH /contacts/<email> met alleen
unsubscribed (een bestaand contact komt dus niet alsnog in het segment; zet het dan met de
hand in Resend).
Het nieuwsbriefvinkje in het klantprofiel (UserProfile.newsletter) staat voor nieuwe
accounts standaard uit en is een voorkeur, geen aanmelding: het zet niemand in Resend.
Klanten die vóór 2026-10-18 een account maakten, hadden dat vinkje ongemerkt aan; zij
staan niet in Resend en moeten zich gewoon aanmelden.
Storingen¶
- "Resend niet ingesteld" in de admin:
RESEND_API_KEYontbreekt of de container is niet herstart. Aanmeldingen en bevestigingen gaan gewoon door; de inhaalslag zet ze in Resend zodra de sleutel er is. - Bevestigd in de admin, niet in Resend: kijk in de Celery-logs naar "Resend weigerde"
(401 "restricted to only send emails": de sleutel heeft geen Full access; anders een
verkeerde sleutel of segment-ID) of "Resend niet bereikbaar". De uurlijkse inhaalslag
probeert het opnieuw; na herstel loopt het vanzelf bij. Meteen inhalen:
docker compose exec -T backend python manage.py shell -c "from apps.users.newsletter import sync_pending; print(sync_pending())". - Wel bij de contacten, niet in het segment: het contact bestond al toen de synchronisatie liep (bijvoorbeeld via een eerdere mail); voeg het in Resend met de hand aan het segment toe.
- Geen bevestigingsmail: zelfde controle als bij bestelmails (Resend → Emails, en de spamfolder). De mail gaat alleen bij een geldig adres en met de schakelaar aan.
- Klant wil eruit zonder link: zet in Resend het contact op unsubscribed. In de admin is er geen knop; de uurlijkse inhaalslag overschrijft de Resend-status niet zolang de aanmelding bij ons niet verandert.