Skip to content

PRD: Lead & subscriber capture

ModuleMarketing (EXT-02)PRD IDPRD-CAP-001
StatusShippedOwnerMarketing squad
Date2026-04-03Versionv1.0
Packages@nx/outreach · @nx/coreURDCAP

TL;DR

Lets the public marketing site collect interested leads - contact/sales/demo inquiries and newsletter subscribers - and gives admins a live read of the subscriber base. Subscribing is idempotent by email, unsubscribe is honored via a token, and an admin statistics endpoint reports totals, monthly growth, and the active/deactivated split. The outcome: a clean, deduplicated audience that respects opt-out, ready to power the campaigns planned for Phase 2.

1. Context & Problem

Merchants want to grow an audience from their public site - visitors who request a demo, contact sales, or opt into the newsletter. Without a capture surface, those signals are lost or scattered across ad-hoc channels: there is no deduplicated subscriber list, no enforced unsubscribe, and no way for an admin to gauge how the audience is growing. That gap blocks any future outbound campaign work, which depends on a clean, opt-out-respecting subscriber base.

This feature establishes the capture foundation in @nx/outreach: it records inquiries and newsletter opt-ins, keeps the subscriber list idempotent and opt-out-safe, and exposes an admin statistics read over the global subscriber list.

2. Goals & Non-Goals

Goals

  • Capture inquiries (contact/sales/demo) submitted from the public site, with a real-time notification to admins.
  • Make newsletter subscribe idempotent by email - re-subscribing reactivates a previously unsubscribed record rather than duplicating it.
  • Honor token-based unsubscribe before any reactivation or send.
  • Expose subscriber statistics for admins: total, new-this-month, activated, and deactivated counts.

Non-Goals

  • Campaigns, drip automation, and A/B testing - owned by Campaigns & Automation (CMP, P2).
  • Email/SMS delivery infrastructure - provided by external providers.
  • Promotional pricing - owned by the Campaign module.

3. Success Metrics

MetricTarget / signal
DeduplicationZero duplicate active subscribers per email
Opt-out integrity100% of unsubscribe tokens resolve to a deactivated record; no send to deactivated subscribers
Capture latencyInquiry submit → admin notification is near real-time
Audience visibilityStatistics endpoint returns total / monthlyNew / activated / deactivated for the global list

4. Personas & Use Cases

PersonaGoal in this feature
VisitorSubmit an inquiry or subscribe to the newsletter from the public site
SubscriberUnsubscribe at any time via a one-click token link
Admin / ManagerSee the subscriber list and its statistics (total, growth, active/deactivated)

Core scenarios: a visitor submits an inquiry (admin is notified in real time) or subscribes to the newsletter (idempotent by email) → a subscriber later clicks the unsubscribe link (token-resolved, deactivated) → an admin reads subscriber statistics over the global list.

5. User Stories

  • As a visitor, I want to submit a contact/sales/demo inquiry from the public site, so the business can follow up.
  • As a visitor, I want to subscribe to the newsletter with my email, so I receive updates - and subscribing twice never creates a duplicate.
  • As a subscriber, I want to unsubscribe via a token link, so I stop receiving messages.
  • As a previously-unsubscribed visitor, I want re-subscribing to reactivate my record, so I rejoin without duplicate entries.
  • As an admin, I want subscriber statistics (total, new-this-month, activated, deactivated), so I can gauge audience growth.
  • As an admin, I want a real-time notification when an inquiry arrives, so I can respond quickly.

6. Functional Requirements

#RequirementURD ref
FR-1Capture an inquiry (contact/sales/demo) submitted from the public siteURD-CAP-001
FR-2Newsletter subscribe is idempotent by email; an active email returns the existing record, no duplicateURD-CAP-002
FR-3Re-subscribing a previously unsubscribed email reactivates it (subscribedAt reset, unsubscribedAt cleared, status ACTIVATED)URD-CAP-002
FR-4Token-based unsubscribe resolves a subscriber by unsubscribeToken and deactivates itURD-CAP-003
FR-5Admin subscriber statistics: total, monthlyNew, and the activated / deactivated split, via a single aggregation over the global subscriber listURD-CAP-004
FR-6A new inquiry submission emits a real-time notification to adminsURD-CAP-001

Full requirement text and acceptance criteria live in the Marketing URD. This PRD references them rather than restating them.

7. Non-Functional Requirements

AreaRequirement
Data integritySubscribe is idempotent by email - one active record per email; soft-deleted rows excluded from statistics
Opt-out integrityUnsubscribe is honored before any reactivation or send; deactivation is via token only
TenancyCapture tables are global (not merchant-scoped) per module constraint C-02; statistics aggregate across all subscribers
AuthzAdmin reads (list, statistics) are permission-gated; public capture endpoints accept JWT/BASIC strategies
PerformanceStatistics is a single COUNT(*) FILTER (...) aggregation query over the subscriber list
i18nSubscriber locale is captured so future sends can localize; user-facing labels are bilingual ({ en, vi })

8. UX & Flows

Key screens: the public-site capture forms (inquiry + newsletter) on the marketing site, and the back-office subscriber list screen (overture-apps/bo) with form-validation UI.

9. Data & Domain

EntityRole
InquiryA contact/sales/demo request captured from the public site (type, name, email, business, subject)
SubscriberA newsletter opt-in - email, locale, topics, status (ACTIVATED/DEACTIVATED), subscribedAt, unsubscribedAt, unsubscribeToken

Conceptual only - full schema and invariants in the outreach developer docs.

10. Dependencies & Assumptions

Depends on

  • @nx/outreach - the capture package hosting inquiry and subscriber controllers, services, and repositories.
  • @nx/core - shared subscriber/inquiry schemas and the core repositories.
  • Real-time notification channel - the outreach WebSocket service that pushes inquiry-submitted events to admins.

Assumptions

  • Capture tables are global (constraint C-02); there is no per-merchant scoping on subscribers/inquiries.
  • Each subscriber holds a unique unsubscribeToken issued at capture time for one-click unsubscribe.
  • External providers (not in scope) handle eventual email/SMS delivery.

11. Risks & Open Questions

Risk / questionMitigation / status
Duplicate subscribers on rapid double-submitSubscribe is idempotent by email - existing active record returned, no insert
Sending to an unsubscribed addressUnsubscribe deactivates the record; constraint C-01 requires opt-out be honored before any send
Global (non-scoped) tables across merchantsAccepted per constraint C-02; statistics are intentionally global
Statistics cost as the list growsSingle aggregation query today; revisit indexing/caching if the list grows large

12. Release Plan & Launch Criteria

AspectPlan
PhaseP1 (foundation) - marketing/CAP in the URD feature catalog
RolloutAll sites; no feature flag
MigrationNone (new entities; global tables)
Launch criteriaIdempotent subscribe verified (no duplicates); token unsubscribe deactivates; statistics return total/monthlyNew/activated/deactivated; inquiry submit fires admin notification
MonitoringSubscribe/unsubscribe volume, duplicate-subscriber rate (expect 0), statistics query latency, inquiry notification delivery

13. FAQ

What happens if the same email subscribes twice? Nothing duplicates - an already-active email returns the existing record; a previously unsubscribed email is reactivated.

How does unsubscribe work? Via a token link: the unsubscribeToken resolves to the subscriber, which is then deactivated. Opt-out is always honored before any send.

Are subscribers scoped per merchant? No - capture tables are global per constraint C-02, so statistics aggregate across all subscribers.

What does the statistics endpoint return? A single aggregation: total, monthlyNew (subscribed since the start of the current month), and the activated / deactivated split, excluding soft-deleted rows.

Does this send any campaigns? No - sending is out of scope here and owned by Campaigns & Automation (CMP, P2). This feature only captures and reports the audience.

References

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.