Catalogue & product data
Newsletter Signup Form
Generated from docs/plan/core/newsletter-signup-form/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
Core form-collection feature enabling merchants to capture email addresses for marketing lists. A simple, embeddable newsletter signup form (name + email) with CAPTCHA protection, subscribing customers to a ‘newsletter’ category. This is foundational infrastructure; email campaign management and segmentation belong elsewhere. Most merchants need this to acquire customers and reduce CAC.
The problem
Merchants can’t build email lists because there’s no newsletter signup. Customer acquisition costs stay high because I can’t follow up with browsing visitors.
What it does
- Newsletter signup form: email field (required), optional name field, optional subscribe-to-category selector
- CAPTCHA integration: reCAPTCHA v3 or hCaptcha, configurable by merchant (API key + secret)
- Form submission: POST /newsletter/subscribe with email, name (optional), category (default ‘newsletter’)
- Double opt-in (optional): send confirmation email, track subscription only after confirmation link clicked
- Single opt-in: store subscription immediately; merchant handles compliance via email disclaimers
- Subscription storage: contact (email, name, subscribed_at, category, verified_email: boolean)
- Unsubscribe link in emails: one-click unsubscribe + landing page showing unsubscribe confirmation
- Bulk import: merchant can import email lists (CSV), mark as verified or unverified
- Export subscriber list: CSV export of all subscribers by category
- Admin dashboard: subscriber count by category, signup rate over time, unsubscribe rate
- Webhook on new subscriber: plugin can listen to ‘newsletter.subscribed’ event
- Rate limiting: prevent spam (e.g., max 5 signups per IP per hour)
- Email validation: reject obviously invalid emails (no @, no TLD), store valid emails only
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- Email campaign creation — that’s marketing platform territory (Mailchimp, Klaviyo); core captures emails, not sends campaigns
- List segmentation by behavior or purchase history — segment by explicit category only; behavioral segmentation belongs in a plugin
- GDPR compliance automation — merchant is responsible for consent language and compliance; core provides opt-in/out mechanism, not legal review
- Spam list enforcement (ISP blacklist checking) — merchant monitors; core validates syntax only
- Third-party ESP sync — manual export/import; automated sync is a plugin integration
Data model
New entity: newsletter_subscription (id, email: string (unique), name: string (nullable), category: string, subscribed_at, verified_at: nullable, unsubscribed_at: nullable, verification_token: string (nullable), token_created_at: timestamp, consent_method: ‘single-opt-in’ | ‘double-opt-in’, consent_logged: boolean, ip_address: string (for rate limiting), user_agent: string (for abuse detection)). No schema migration required; starts empty.
API
- POST /newsletter/subscribe — accept {email, name?, category?}, return {success: boolean, message: string, verify_email_required: boolean}
- GET /newsletter/verify/:token — verify email subscription (double opt-in flow)
- POST /newsletter/unsubscribe/:email — unsubscribe via link, return confirmation page
- GET /newsletter/subscribers — list all subscribers (admin only), optionally filtered by category
- GET /newsletter/subscribers/export — export subscribers as CSV (admin only)
- POST /newsletter/subscribers/import — bulk import subscriber list (CSV, admin only)
- GET /newsletter/stats — signup rate, subscriber count, unsubscribe rate over time (admin only)
- PUT /newsletter/settings — configure CAPTCHA, opt-in method (single vs double), from_email for verifications
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Newsletter management panel includes: (1) Signup form embed code (HTML snippet for storefronts), (2) Subscriber list (sortable by date, filterable by category), (3) Export/import buttons, (4) Double opt-in toggle + email template editor, (5) Unsubscribe landing page customizer, (6) Settings (CAPTCHA keys, from_email address), (7) Metrics dashboard (new signups per day, unsubscribe rate, category breakdown).
The seam — why this is core
Core owns the subscription form, storage, opt-in/out mechanism, and export. This is foundational infrastructure for every merchant. Email campaign sending, segmentation, and marketing automation are owned by plugins or third-party integrations (Mailchimp, Klaviyo). Core does not gate merchants without email campaigns; the form works standalone.
Core owns the interface + honest form collector; email list capture is basic infrastructure, not a per-country obligation or credential.
Dependencies
- email-layer (core; to send verification and unsubscribe-confirm emails)
- captcha-integration (core; to protect form from spam)
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- A POST /newsletter/subscribe with email=‘test@example.com’, name=‘Alice’ stores the subscription and returns {success: true, verify_email_required: false} (single opt-in mode).
- With double opt-in enabled, POST /newsletter/subscribe sends a verification email with a token link; subscription.verified_at remains null until the token is verified.
- Clicking the verify token link calls GET /newsletter/verify/:token and sets verified_at; only then is the email considered subscribed.
- An unsubscribe link /newsletter/unsubscribe/test@example.com sets unsubscribed_at and shows a confirmation page.
- Rate limiting rejects a sixth signup from the same IP within an hour, returning a 429 error.
- Exporting subscribers as CSV includes email, name, subscribed_at, verified_at, category columns.
- A subscriber unsubscribed in the past (unsubscribed_at is not null) does not receive future signup welcome emails if they re-subscribe.
- Invalid email ‘alice@invalid’ is rejected; ‘alice@example.com’ is accepted and stored.
Risks
If CAPTCHA is misconfigured or disabled, bots flood the subscriber list with spam. If unsubscribe links are not honored, merchants risk spam complaints and ISP blacklisting. A merchant using single opt-in without clear consent language violates GDPR; AstroBaaS must provide warnings but cannot enforce merchant compliance. Exporting a subscriber list for use in a third-party ESP requires matching emails exactly; a mismatch silently creates duplicates.
Commercial context
| Suggested price | free (core) |
| Rival anchor | Shopify: Mailchimp (free tier); Magento: included |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.