Hoppa till huvudinnehåll
Petanque Life

Newsletter Engine (super-premium, multi-list, multi-tenant)

F19.17 23 features Levererad

I korthet

The newsletter engine is a fully owned, super-premium marketing-email platform — multi-list, multi-tenant, MJML templates, visual segment builder, drip automations, A/B testing with statistical winner selection, per-recipient send-time optimisation, Gemini-powered AI assist, GDPR-strict double opt-in and Litmus cross-client previews — replacing Mailchimp, ConvertKit and Customer.io without losing a single feature.

Så fungerar det

Petanque Life owns its email channel rather than renting it. An EmailDistributor abstraction (Mailjet primary, ACS failover, Null in dev) handles deliverability, while every other concern — subscribers, lists, campaigns, opens, clicks, segments, preference centre, automations, suppressions — lives in the platform's own MongoDB. Lists are scoped platform-wide or per tenant.

Subscribers move through pending → active → unsubscribed/bounced/complained/suppressed. Templates are MJML source plus a rendered HTML cache. Campaigns follow draft → scheduled → sending → sent.

Events live for 90 days under a TTL index. Double opt-in uses HMAC-SHA256 signed, purpose-bound tokens (confirm/prefs/unsub, TTL 14/30/90 days). Public endpoints cover subscribe (Turnstile + honeypot + 5/IP/h rate-limit), confirm, preferences, unsubscribe and RFC 8058 one-click list-unsubscribe, plus tracking pixels and click-redirects.

Sys-CRUD covers lists, subscribers, CSV import with attested consent proof, GDPR deletion, MJML editor, visual segment builder, campaign wizard, automations, suppressions and deliverability. A/B testing uses pooled two-proportion z-test with Wilson confidence intervals; a winner-selection job picks the winner once significance is reached. Send-time optimisation builds an engagement_profile per subscriber (best_hour_utc, best_weekday) refreshed daily; the dispatcher schedules each recipient inside their optimal 24h window.

Drip automations use a react-flow visual builder with preset onboarding, re-engagement and win-back flows. Gemini 2.5 Pro powers an AI assist (subject lines, content polish, 5-persona preview) under a 50 req/operator/24h quota with prompt and response hashes audit-logged. Heatmaps aggregate click coordinates per campaign; cohorts compute engagement scores per subscriber and flag at-risk subscribers for re-engagement.

Litmus runs cross-client previews and spam-score checks, blocking schedule when spam score > 5. The campaign dispatcher runs every minute, throttles to throttle_per_hour, renders MJML, merges variables, rewrites links, injects the open pixel, appends the compliance footer and sets List-Unsubscribe headers. The distributor webhook verifies HMAC, dedupes on provider_event_id, suppresses on hard bounce, escalates after 5 consecutive soft bounces, and suppresses + flags on complaints.

Twelve premium templates ship out of the box. Embeddable signup forms exist in both Astro and Next.js with prop-driven layouts and 4-language i18n. Compliance is non-negotiable: consent proof per subscribe, CAN-SPAM physical address auto-injected, full audit trail, 3-year post-unsub retention.

A deliverability dashboard auto-halts sends when bounce > 2% or complaint > 0.1%.

Centrala funktioner

  • EmailDistributor abstraction (Mailjet primary, ACS failover, Null dev) — provider-agnostic delivery
  • HMAC-signed double opt-in with purpose-bound tokens and RFC 8058 one-click unsubscribe
  • Visual segment builder and react-flow drip automations (onboarding, re-engagement, win-back)
  • A/B testing with z-test and Wilson intervals; per-subscriber send-time optimisation
  • Gemini-powered AI assist for subject lines, content polish and 5-persona previews
  • Cross-client preview and spam-score check via Litmus, blocking sends above threshold
  • Compliance baked in: consent proof, CAN-SPAM footer, audit trail, deliverability auto-halt

I praktiken

A marketer drafts a federation announcement campaign in the sys console. She picks the 'federation_update' premium template, edits MJML in the visual editor, and uses the AI assist to generate four subject-line variants — picks two for an A/B split. She drags a segment in the visual builder ('country in [FR, BE, CH] AND role = federation_admin AND active in last 90 days'), sees 1 247 recipients live.

She enables send-time optimisation, schedules for tomorrow morning, runs a Litmus preview (spam score 1.4, all clients render clean). The dispatcher fans out across the day inside each subscriber's optimal window. The winner-selection job picks variant B at 04:00 the next morning and sends it to the remaining 80% pool — open rate 38%, click rate 7%, complaints 0.02%.

Features i detta subsystem

23
ID Status Funktioner
F19.17.01 Levererad EmailDistributor-abstraktion med send_transactional / send_bulk / handle_webhook; MailjetDistributor + ACSDistributor (failover) + NullDistributor (dev/test) ✅ PL-T152
F19.17.02 Levererad Beanie-modeller: NewsletterList (platform/tenant-scope), NewsletterSubscription (pending/active/unsubscribed/bounced/complained/suppressed), NewsletterTemplate (MJML-source + rendered cache), NewsletterSegment, NewsletterCampaign (draft/scheduled/sending/sent/paused/cancelled), NewsletterEvent (90 d TTL), NewsletterSuppression, NewsletterAutomationFlow, NewsletterAutomationEnrolment, NewsletterAIQuota ✅ PL-T152
F19.17.03 Levererad Double-opt-in med HMAC-SHA256-signerade tokens (purpose-bound: confirm/prefs/unsub, TTL 14/30/90 d) — services/newsletter_tokens.py ✅ PL-T152
F19.17.04 Levererad Publika endpoints: POST /public/newsletter/subscribe (Turnstile + honeypot + rate-limit 5/IP/h), GET /public/newsletter/confirm, GET/PUT /public/newsletter/preferences/{token}, POST /public/newsletter/unsubscribe/{token}, POST /public/newsletter/unsubscribe-all/{token}, POST /public/newsletter/list-unsubscribe/{token} (RFC 8058 one-click), GET /public/newsletter/open/{...}.png, GET /public/newsletter/click/{...}?url= ✅ PL-T152
F19.17.05 Levererad Sys-CRUD: lists + subscribers + CSV-import med attesterad consent-proof + GDPR-radering + templates (MJML-editor) + segments (visual builder) + campaigns (wizard) + automations + suppressions + deliverability + analytics-overview ✅ PL-T152
F19.17.06 Levererad A/B-testing: variants + random-N% / full-random-split + pooled two-proportion z-test + Wilson confidence interval + winner-selection-job (services/newsletter_stats.py) ✅ PL-T152
F19.17.07 Levererad Send-time-optimering: engagement_profile = {best_hour_utc, best_weekday, confidence} per subscriber; dagligt refresh-job + dispatcher som schemalägger per-recipient inom 24h-fönster, fallback till scheduled_at vid confidence < 0.3 ✅ PL-T152
F19.17.08 Levererad Drip-automations: AutomationFlow med visual flow-builder (react-flow, trigger/wait/send/branch/exit); preset-flows: 7-day onboarding, 30-day re-engagement, 90-day win-back; triggers: subscription_confirmed, webinar_registered, tenant_onboarded, user_inactive_days, manual_enroll ✅ PL-T152
F19.17.09 Levererad Dynamic content blocks i MJML: {% content-block key variants=[...] %} med send-time-evaluation per subscriber-context (språk/tenant/merge_data) ✅ PL-T152
F19.17.10 Levererad AI-assist via Gemini 2.5 Pro: /sys/newsletter/ai/subject-lines, /ai/content-polish, /ai/preview-audience-reaction (5 personas); quota 50 req/operator/24h via newsletter_ai_quota; alla calls audit-loggade med prompt+response-hash ✅ PL-T152
F19.17.11 Levererad Heatmaps + cohort-analytics: click-koordinat-aggregation per campaign, open/click-rate per cohort (tenant/role/language/source), engagement-score per subscriber (rolling 30d), at-risk-flagg (< 0.1 → feeder till re-engagement-automation) ✅ PL-T152
F19.17.12 Levererad Cross-client-preview via Litmus API: screenshots per klient (Gmail, Apple Mail, Outlook, Yahoo) + spam-score-check; blockerar schedule vid spam-score > 5 ✅ PL-T152
F19.17.13 Levererad Visuell segment-builder: drag-and-drop AND/OR-grupper, live recipient-count, sparbara segments; allow-list av fält + ops (eq, neq, in, nin, gt/gte/lt/lte, regex escaped, exists); services/newsletter_segment.py ✅ PL-T152
F19.17.14 Levererad Campaign-dispatcher-job (newsletter-campaign-dispatcher, 1 min-cadence): throttled send enligt throttle_per_hour (default 5000/h); per recipient: MJML-render + merge-vars + link-rewrite + pixel-inject + compliance-footer + List-Unsubscribe-header + RFC 8058-one-click; pause/resume/cancel ✅ PL-T152
F19.17.15 Levererad Distributor-webhook: POST /_internal/distributor-webhook/{provider} med HMAC-signaturverifiering, idempotent på provider_event_id; hard_bounce → suppression; soft_bounce-escalation efter 5 consecutive; complaint → suppression + status=complained ✅ PL-T152
F19.17.16 Levererad Premium-template-gallery (12+ pre-designed): announcement, digest, event_invite, product_drop, case_study, win_back, re_engagement, onboarding_step, tournament_recap, scoreboard_release, federation_update, seasonal — clone-to-customize ✅ PL-T152
F19.17.17 Levererad Embeddable signup-form — Astro (www/src/components/conversion/NewsletterSignup.astro) + Next.js (web/src/components/blocks/NewsletterSignupBlock.tsx); prop listSlug, layouts (inline/modal/card), 4-språk i18n, Turnstile + honeypot ✅ PL-T152
F19.17.18 Levererad Preference-center-sida: www/src/pages/{en,sv,fr,es}/newsletter/preferences.astro med query-string-token, lista av lists i samma scope, name/language/per-list-membership, unsubscribe-all ✅ PL-T152
F19.17.19 Levererad Webinar-integration (T151): GDPR-strikt default-unticked opt-in på POST /public/webinars/{slug}/register; vid kryss skapas NewsletterSubscription(status=pending, source=webinar_registration) parallellt med webinar-confirmation ✅ PL-T152
F19.17.20 Levererad Tenant-admin read-only-vy: admin/app/(dashboard)/newsletter/{index,[id]}.tsx + /admin/newsletter/{lists,lists/{id}/subscribers,campaigns}-API scoped via request.state.tenant_id (403 utan tenant-kontext) ✅ PL-T152
F19.17.21 Levererad Compliance: consent-proof per subscribe (IP-hash + UA-hash + form_id + timestamp + method), CAN-SPAM physical-address auto-inject i footer, audit-trail för subscribe/confirm/preferences/unsubscribe/import/suppression/GDPR, 3-year post-unsub retention ✅ PL-T152
F19.17.22 Levererad Deliverability-dashboard: per from-domain reputation + SPF/DKIM/DMARC-status + bounce/complaint/unsub 30d-rater + spam-score-historik; halt-sends-action vid threshold-breach (bounce > 2 % eller complaint > 0.1 %) ✅ PL-T152
F19.17.23 Levererad Tester: 35 testfall i api/tests/test_t152_newsletter.py — unit (z-test, Wilson, pick_winner, tokens, segment DSL), integration (full subscribe→confirm→prefs→unsub-cykel, suppression, honeypot, rate-limit 429, tenant-admin 403), alla passerar ✅ PL-T152