Customers & accounts
Customer Preferences
Generated from docs/plan/core/customer-preferences/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
Customer Preferences lets customers manage email frequency, communication channels (email/SMS), and content types (promotions, product updates, transactional) from their account profile or via email unsubscribe links.
The problem
Customers can’t control communication frequency; merchants email them daily and they unsubscribe.
What it does
- Email opt-in/opt-out
- SMS opt-in/opt-out
- Preference categories: promotions, product updates, transactional
- Frequency setting: daily digest, weekly, immediate (per category)
- Preference center: self-serve page (accessible from email footer)
- One-click unsubscribe: link in email, no login needed
- Subscription management: re-subscribe after unsubscribe
- Audit log: preference changes
- Admin override: staff can mark unsubscribed (e.g., bounced)
- Transactional emails: never gated (always sent)
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.
- Preference sync to email provider — email-marketing-automation-segmentation scope
- Per-email-type unsubscribe (just this digest) — categories + frequency only
- Preference history timeline — audit log shows current state
- Preference prediction (likely to unsubscribe) — analytics/ML
- List import/export with external tools — send-only
Data model
CustomerPreference: customer_id, email_opted_in, sms_opted_in, prefer_promotions, prefer_updates, prefer_transactional (always true), frequency, updated_at, opted_in_at, opted_out_at. UnsubscribeToken: customer_id, token_hash, created_at, expires_at. Migration: add preferences and unsubscribe_tokens tables.
API
- GET /storefront/preferences
- PATCH /storefront/preferences
- GET /storefront/unsubscribe/{token}
- POST /storefront/preferences/resubscribe
- GET /admin/customers/{id}/preferences
- PATCH /admin/customers/{id}/preferences
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Customer detail: Communication Preferences card (email on/off, SMS, categories, frequency, last updated). Override button: mark unsubscribed or force re-subscribe (with audit).
The seam — why this is core
Core owns: CustomerPreference table, preference UI, unsubscribe token generation. Paid module owns: email-marketing-automation-segmentation reads preferences when sending campaigns.
Core owns the interface + honest preference manager; customer control is infrastructure, not a support commitment or credential.
Dependencies
- customer-profiles
- email-marketing-automation-segmentation (optional)
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.
- Unchecking Promotions causes email_opted_in=true but prefer_promotions=false
- Sending promotion to all; email delivery skips prefer_promotions=false
- Transactional (order confirmation) always sent even if email_opted_in=false
- Clicking unsubscribe in email (one-click, token) lands on ‘Unsubscribed successfully’, no login
- Clicking resubscribe in follow-up succeeds
- Accessing preference center without login but with valid unsubscribe token; saving marks opted-in again
- Frequency=weekly stored; email module batches sends
- Staff marks customer bounced (admin override) causes email_opted_in=false; audit shows who did it
Risks
Unsubscribe token never expires. Token guessed. Wrong customer if token mismatch. Transactional flag configurable causes order emails not sent. Audit lacks field-level changes. Re-subscribe restores all preferences.
Commercial context
| Suggested price | free (core) |
| Rival anchor | Shopify: included; 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.