Hyphen User Guides

Marketing Features

Complete guide to audience management, campaigns, email marketing, promo codes, and conversion surfaces

Version 1.0|Updated 2026-04-12|Marketing Teams, Campaign Managers, Audience Teams, Operations
Marketing hub dashboard
1
2
3
4
The marketing hub — your starting point for all marketing features
1Segments — build targeted audience groups with filters
2Campaigns — create and manage outreach campaigns
3Email Campaigns — send templated emails to segments
4Promo Codes — generate discount codes for campaigns

1. Executive Overview

What It Does

The Marketing Features module provides a comprehensive marketing, outreach, and audience engagement system for the Hyphen Publishing Platform. It enables publication administrators to:

  • Build and manage audience contact lists from CSV imports, campaign submissions, and newsletter signups
  • Create targeted user segments with advanced filtering across both registered readers and external audience contacts
  • Launch outreach campaigns with customizable landing pages, dynamic forms, event content, incentives, and social integration
  • Send email campaigns to segmented audiences using templated emails with engagement tracking
  • Manage newsletter editions authored in Strapi CMS, sent as email campaigns, and archived for readers
  • Create and distribute promotional codes with configurable discount types and validity rules
  • Deploy conversion surfaces (modals, banners, slide-ins) with targeting rules and trigger-based activation on the Reader Portal
  • Track engagement analytics across campaigns, email deliveries, and conversion surfaces with integrated GA4 and internal metrics

Who Uses It

RoleAccess Level
Marketing AdminFull access: create, edit, publish, delete campaigns; manage audience, segments, email campaigns, promo codes, conversion surfaces
Editor / Content ManagerCampaign read access; may publish campaigns depending on RBAC
ViewerRead-only access to campaigns and metrics
Reader (Public)Accesses campaign landing pages, newsletter archive, receives emails/notifications

Business Problem It Solves

Publications need to grow their readership, retain subscribers, and promote events/content. This module provides a unified platform to:

  • Acquire new readers through outreach campaigns (student programs, events, lead generation)
  • Convert anonymous visitors to subscribers through strategically-placed conversion surfaces
  • Engage existing audiences through segmented email campaigns and newsletters
  • Track marketing ROI through integrated analytics across all channels

2. Feature Scope

In Scope

FeatureStatus
Audience Contact Management (CRUD, CSV import/export)Implemented
User Segment Builder (advanced multi-source filtering)Implemented
Outreach Campaign CRUD (7-step wizard, 5+ campaign types)Implemented
Campaign Landing Pages (reader portal, template-based)Implemented
Campaign Publishing Workflow (draft → active → completed → archived)Implemented
Campaign Submissions & Review (approval/rejection with email)Implemented
Campaign Invitations (segment-targeted email invitations)Implemented
Email Campaigns (template-based, scheduled/immediate send)Implemented
Newsletter Editions (Strapi CMS content, send-as-campaign bridge)Implemented
Promotional Codes (percentage/fixed/trial_days, auto-generation)Implemented
Conversion Surfaces (6 surface types, 7 trigger types, targeting)Implemented
Campaign Types (admin-manageable, seeded from enum)Implemented
Social Media Integration (social post creation from campaigns)Implemented
QR Code Generation (for campaign landing pages)Implemented
Campaign Statistics & AnalyticsImplemented
Email Engagement Webhooks (SendGrid, Mailchimp)Implemented
Cron-based Automation (scheduled emails, campaign lifecycle, invitations)Implemented

Out of Scope

  • A/B testing for conversion surfaces (planned for future)
  • Geographic targeting for conversion surfaces (planned)
  • Webhook events for campaign lifecycle (planned)
  • Content-aware inline embed positioning (planned)
  • Admin preview mode for conversion surfaces (planned)
ModuleDependency Type
User & Subscription Management (04)Segments query Reader and Subscription models; promo codes create subscriptions
Admin Settings & RBAC (11)All marketing endpoints enforce permission-based access control
Notifications & Email System (14)Email campaigns use shared email service (SendGrid/SMTP); webhooks update delivery metrics
Reader Portal (13)Campaign landing pages, newsletter archive, conversion surfaces render here
Social Media Management (08)Campaigns link to SocialCampaign for cross-channel posting
Homepage Layout & Templates (03)Campaign pages extend the page template system (DynamicTemplate, SectionRenderer)
E-Commerce / Shopify (09)Conversion surfaces can link to products
Strapi CMSNewsletter editions are authored and stored in Strapi

3. Roles & Permissions

Marketing Permissions

Permission KeyDescriptionRequired For
MARKETING_SEGMENTS_READView segmentsSegments list, segment detail
MARKETING_SEGMENTS_CREATECreate segmentsNew segment
MARKETING_SEGMENTS_UPDATEEdit segmentsEdit segment filters
MARKETING_SEGMENTS_DELETEDelete segmentsRemove unused segments
MARKETING_CAMPAIGNS_READView campaigns, audience, promo codesCampaign list, detail, audience list
MARKETING_CAMPAIGNS_CREATECreate outreach campaignsNew campaign wizard
MARKETING_CAMPAIGNS_UPDATEEdit campaigns, publish/unpublishEdit campaign, status transitions
MARKETING_CAMPAIGNS_DELETEDelete campaignsRemove campaigns
MARKETING_SUBMISSIONS_READView submissionsSubmissions tab
MARKETING_SUBMISSIONS_REVIEWApprove/reject submissionsSubmission approval workflow
MARKETING_SUBMISSIONS_EXPORTExport submission dataCSV export
MARKETING_EMAIL_CAMPAIGNS_READView email campaignsEmail campaign list
MARKETING_EMAIL_CAMPAIGNS_CREATECreate email campaignsNew email campaign
MARKETING_EMAIL_CAMPAIGNS_UPDATEEdit email campaignsEdit draft campaigns
MARKETING_EMAIL_CAMPAIGNS_DELETEDelete email campaignsRemove unsent campaigns
MARKETING_EMAIL_CAMPAIGNS_SENDSend/schedule email campaignsSend action
Permission KeyUsed By
SETTINGS_READView email templates and preferences
SETTINGS_UPDATECreate/edit email templates and preferences

RBAC Configuration

Permissions are assigned to roles in Admin Console → Settings → Roles & Permissions. Each role can be granted specific marketing permissions. The Marketing Hub page itself controls card visibility based on permissions — users without MARKETING_CAMPAIGNS_READ will not see the Campaigns card.


4. Architecture & Design Overview

System Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        ADMIN CONSOLE                            │
│  ┌──────────┐ ┌──────────────┐ ┌────────────┐ ┌─────────────┐ │
│  │ Marketing│ │  Email       │ │  Segments  │ │  Conversion │ │
│  │ Hub      │ │  Campaigns   │ │  Builder   │ │  Surfaces   │ │
│  └────┬─────┘ └──────┬───────┘ └─────┬──────┘ └──────┬──────┘ │
│       │              │               │               │         │
│  ┌────┴──────────────┴───────────────┴───────────────┴──────┐  │
│  │              /api/marketing/*  (REST APIs)               │  │
│  └────┬──────────────┬───────────────┬───────────────┬──────┘  │
│       │              │               │               │         │
│  ┌────┴──────────────┴───────────────┴───────────────┴──────┐  │
│  │                  Prisma ORM (PostgreSQL)                  │  │
│  │  OutreachCampaign | EmailCampaign | UserSegment |        │  │
│  │  AudienceContact | ConversionSurface | etc.              │  │
│  └──────────────────────────────────────────────────────────┘  │
│       │                                                        │
│  ┌────┴────┐  ┌───────────┐  ┌──────────────┐                │
│  │ Strapi  │  │ SendGrid/ │  │  S3 Storage  │                │
│  │  CMS    │  │  SMTP     │  │  (uploads)   │                │
│  └─────────┘  └───────────┘  └──────────────┘                │
└─────────────────────────────────────────────────────────────────┘
         │                         │
         ▼                         ▼
┌─────────────────────────────────────────────────────────────────┐
│                       READER PORTAL                             │
│  ┌──────────────┐  ┌──────────────┐  ┌───────────────────────┐ │
│  │  Campaign    │  │  Newsletter  │  │  Conversion Surface   │ │
│  │  Landing     │  │  Archive     │  │  Renderer (global)    │ │
│  │  Pages       │  │  Pages       │  │  Modals/Banners/etc.  │ │
│  └──────────────┘  └──────────────┘  └───────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

Key Entities & Models

ModelPurposeStorage
OutreachCampaignOutreach campaigns with landing page config, form fields, event detailsPrisma
CampaignSubmissionForm submissions from campaign landing pagesPrisma
CampaignInvitationEmail invitations sent to segments for campaignsPrisma
CampaignTypeAdmin-manageable campaign type definitionsPrisma
CampaignNotifyRequest"Notify me" requests from expired campaign visitorsPrisma
UserSegmentAudience segment with JSON filter criteriaPrisma
AudienceContactExternal contacts imported via CSV or created from submissionsPrisma
EmailCampaignEmail campaigns with template, segment targeting, delivery metricsPrisma
EmailTemplateReusable email templates with Handlebars variable supportPrisma
PromotionalCodeAuto-generated promo codes linked to campaignsPrisma
DiscountCouponAdmin-created reusable discount codesPrisma
ConversionSurfacePromotional surfaces (modals, banners) with targeting/trigger configPrisma
ConversionEventInteraction events (impression, click, dismiss, conversion)Prisma
NewsletterEditionNewsletter content editionsPrisma (local mirror) + Strapi (authoring)

Workflow / State Models

Outreach Campaign Status Flow:

draft ──publish──→ active ──unpublish──→ paused ──publish──→ active
                     │                      │
                  complete               archive
                     │                      │
                     ▼                      ▼
                 completed ──archive──→ archived

Email Campaign Status Flow:

draft ──schedule──→ scheduled ──cron trigger──→ sending ──→ sent
  │                                                  │
  └──send now──→ sending ──────────────────────→ sent/failed

Campaign Submission Status Flow:

pending_review ──approve──→ approved
       │                        │
       ├──reject───→ rejected   └──generate promo──→ (with PromotionalCode)

       └──request info──→ needs_info ──resubmit──→ pending_review

Conversion Surface Status Flow:

draft ──activate──→ active ──pause──→ paused ──activate──→ active
  │                   │                  │
  └──schedule──→ scheduled             archive
                  │                      │
                  └──cron──→ active      ▼
                      │              archived
                   expires


                   expired

5. Prerequisites & Setup Requirements

Critical Prerequisites (Must be configured before marketing features work)

1. Email Provider Configuration

Marketing relies heavily on email delivery. Without a configured email provider, email campaigns, invitations, and submission notifications will fail.

SettingLocationRequired
SENDGRID_API_KEYEnvironment variableYes (if using SendGrid)
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSEnvironment variableYes (if using SMTP)
EMAIL_FROM_ADDRESSEnvironment variableYes
EMAIL_FROM_NAMEEnvironment variableYes

Verify: Admin Console → Settings → Email → SMTP Status. Should show configured: true.

2. Email Templates

Campaign-related emails (approval, rejection, invitation, notification) require email templates to be seeded.

Action: Seed default templates by calling POST /api/marketing/email-templates/seed or creating them manually in Admin Console → Settings → Email Templates.

Required template keys:

  • campaign_submission_approved
  • campaign_submission_rejected
  • campaign_invitation
  • campaign_notify_new_campaign

3. Email Preferences & Branding

Configure email branding (logo, colors, publication name, footer, unsubscribe link) in Admin Console → Settings → Email Preferences to ensure all outgoing emails look professional and comply with CAN-SPAM requirements.

4. Strapi CMS Configuration (for Newsletters only)

Newsletter editions are authored in Strapi. Required settings:

SettingLocationRequired For
STRAPI_URLEnvironment variableNewsletter CRUD
STRAPI_API_TOKENEnvironment variableNewsletter CRUD

Newsletter editions content type must exist in Strapi with fields: title, slug, previewText, content, plainTextContent, editionNumber, topic, publishDate, coverImage, status, isFeatured, priority.

5. S3 Storage Configuration (for file uploads)

Campaign form submissions may include file uploads (selfies, ID cards, documents).

SettingLocationRequired For
AWS_S3_BUCKETEnvironment variableCampaign file uploads
AWS_ACCESS_KEY_IDEnvironment variableCampaign file uploads
AWS_SECRET_ACCESS_KEYEnvironment variableCampaign file uploads
AWS_REGIONEnvironment variableCampaign file uploads

6. Cron Job Configuration

Automated tasks require the cron system to be running.

Cron EndpointPurposeRecommended Interval
GET /api/cron/send-scheduled-emailsSend due email campaignsEvery 1-5 minutes
GET /api/cron/campaign-statusAuto-complete expired campaigns, send notificationsEvery 15-30 minutes
GET /api/cron/send-invitationsSend scheduled invitationsEvery 5 minutes
GET /api/cron/expire-invitationsExpire overdue invitationsEvery hour
GET /api/cron/invitation-remindersSend weekly invitation remindersWeekly

All cron endpoints require: Authorization: Bearer <CRON_SECRET>

7. Webhook Configuration (for email engagement tracking)

To track email opens, clicks, bounces, and unsubscribes:

SendGrid:

  • Register webhook URL: {ADMIN_CONSOLE_URL}/api/webhooks/sendgrid
  • In SendGrid Dashboard → Settings → Mail Settings → Event Webhook
  • Optional: Set SENDGRID_WEBHOOK_VERIFICATION_KEY for signature verification

Mailchimp/Mandrill:

  • Register webhook URL: {ADMIN_CONSOLE_URL}/api/webhooks/mailchimp
  • In Mandrill Dashboard → Settings → Webhooks
  • Optional: Set MAILCHIMP_WEBHOOK_KEY for HMAC-SHA1 verification

8. Reader Portal URL Configuration

Campaign landing pages render on the Reader Portal. The Admin Console needs to know the Reader Portal URL for preview links and public campaign URLs.

SettingLocation
NEXT_PUBLIC_READER_URLAdmin Console environment variable
NEXT_PUBLIC_ADMIN_API_URLReader Portal environment variable (to fetch campaign data from Admin API)

9. RBAC Permission Assignment

Ensure the appropriate roles have marketing permissions assigned. Navigate to Admin Console → Settings → Roles & Permissions and grant the marketing permission group to roles that need access.

10. GA4 Configuration (for conversion surface analytics)

Conversion surfaces fire custom GA4 events. If you use Google Analytics:

SettingLocation
NEXT_PUBLIC_GA_MEASUREMENT_IDReader Portal environment variable

GA4 events tracked: conversion_surface_impression, conversion_surface_click, conversion_surface_dismiss, conversion_surface_conversion, coupon_copied, newsletter_signup, registration_start.


6. Sub-Module: Audience Contacts

Audience contacts list
The audience contacts page shows imported and collected contacts with search, filters, and segment assignment.

Purpose

Manage external audience contacts (people not yet registered as readers) for use in segments, campaign invitations, and email campaigns.

Access

Admin Console → Marketing → Segments → Contacts tab (Also accessible via Marketing → Audience, which redirects to the Contacts tab)

Key Features

6.1 View Contacts

  • Table view with columns: Name, Email, Phone, Institution, Tags, Source, Created
  • Search by name or email
  • Filter by tags
  • Sort by any column
  • Pagination

6.2 Add Single Contact

  1. Click "Add Contact" button
  2. Fill in fields: Email (required), Name, Phone, Institution, Designation, City, State, Country, Tags
  3. Click Save
  4. Contact is created with source: 'manual'

6.3 Bulk Import via CSV

  1. Click "Upload CSV" button
  2. Drag-and-drop or browse to select a CSV file
  3. Column Mapping Step: The system auto-detects columns. Map CSV columns to contact fields (email, name, phone, institution, etc.)
  4. Preview Step: Review first 5 rows of mapped data
  5. Click Upload
  6. System upserts contacts by email (creates new, updates existing)
  7. Results summary shows: created, updated, skipped, errors

CSV Requirements:

  • Must have an email column (required)
  • All other columns are optional
  • Duplicate emails are updated (not duplicated)
  • Tags can be specified as comma-separated values

6.4 Edit Contact

  • Click any contact row to open the Edit Contact modal
  • Modify fields and click Save

6.5 Delete Contact

  • Select one or more contacts → click Delete in bulk actions
  • Or click delete icon on individual row
  • Soft delete by default (isActive = false)
  • Hard delete available with ?hard=true query parameter (API only)

6.6 Export Contacts

  • Click Export to download all active contacts as CSV

6.7 Auto-Created Contacts

Contacts are automatically created (upserted) from:

  • Campaign form submissions: Tagged campaign_submission, with source detail of campaign name
  • Newsletter signups: Tagged newsletter_signup

These auto-created contacts appear in the Contacts tab and can be included in segments.

Business Rules

  • Email must be unique across all contacts
  • Contacts with isActive = false are excluded from segments and email sends
  • Unsubscribed contacts (unsubscribedAt set) are excluded from non-forced email sends
  • A contact can be linked to a Reader record via readerId (one-to-one)

7. Sub-Module: User Segments

User segments list
Create and manage audience segments with advanced filtering for targeted campaigns and communications.

Purpose

Create targeted audience groups by combining filter criteria across both registered readers and audience contacts. Segments are used for email campaigns and campaign invitations.

Access

Admin Console → Marketing → Segments → Segments tab

Key Features

7.1 View Segments

  • List view with columns: Name, Member Count, Filters (summarized), Created By, Last Counted
  • Search by segment name
  • Click any segment to view/edit

7.2 Create Segment

  1. Click "New Segment"
  2. Enter segment Name and optional Description
  3. Build Filters using the sidebar filter groups:
Filter GroupFilter Options
Contact SourceReaders, Audience Contacts, All
CampaignParticipated in specific campaign
User TierFree, Basic, Premium, etc.
Account StatusActive, Inactive, Banned
Subscription StatusActive, Expired, Cancelled, Trial
Email VerifiedYes / No
Newsletter Opt-InSubscribed / Not subscribed
Audience TagsTags assigned to audience contacts
Registration DateDate range filter
Last Active DateDate range filter
  1. Filters use AND logic between groups and OR logic within a group
  2. The right sidebar shows a live member count (debounced, updates as you change filters)
  3. Click "Preview Members" to see a sample of matching members
  4. Click Save Segment

7.3 Edit Segment

  • Open segment → modify filters → member count updates in real-time → Save

7.4 Preview Members

  • Click "Preview Members" or open the segment → see paginated list of matching members
  • Shows both readers and audience contacts with source labels
  • Can exclude specific members from the segment preview

7.5 Export Segment

  • "Export" downloads segment members as CSV
  • Includes both readers and audience contacts with a source column

7.6 Delete Segment

  • Only allowed if the segment is not used by active campaigns or invitations
  • Attempting to delete a segment in use shows an error message

Business Rules

  • Segment member count is auto-calculated when filters change
  • Segments resolve members dynamically at query time (not static lists)
  • When Contact Source includes "Audience Contacts" or "All", the segment queries both Reader and AudienceContact tables
  • The lastCountedAt timestamp tracks when the member count was last refreshed
  • Segment filter criteria are stored as a JSON object on the UserSegment model

8. Sub-Module: Outreach Campaigns

Campaigns list
The campaigns page shows all outreach campaigns with status, type, dates, and quick actions.

Purpose

Create multi-purpose outreach campaigns with customizable landing pages, dynamic forms, incentives, event content, and post-event materials.

Access

Admin Console → Marketing → Campaigns

Campaign Types

TypeUse Case
student_outreachStudent recruitment programs (with institution verification)
eventConference, webinar, workshop registration (with rich event content)
lead_genLead generation with form capture
institutionalInstitutional partnership campaigns
generalGeneral-purpose outreach

Additionally, admins can create custom campaign types via the Campaign Types management page.

8.1 Create Campaign (7-Step Wizard)

Navigate to Marketing → Campaigns → New Campaign

Step 1: Basics

  • Campaign Name (required)
  • URL Slug (required, unique, auto-generated from name)
  • Campaign Type (select from admin-managed types)
  • Description
  • Start Date (required)
  • End Date (required)

Step 2: Form Fields Build the campaign registration form using the form field builder:

  • Add fields with types: text, email, phone, url, number, date, textarea, select, radio, checkbox, photo_selfie, photo_id_card, file_upload, institution_picker
  • Configure per field: label, placeholder, required, help text, validation rules (minLength, maxLength, min, max, pattern), options (for select/radio/checkbox)
  • Reorder fields via drag-and-drop
  • Also configure Notify Form fields (shown on expired campaigns)

Step 3: Landing Page

  • Landing Page Title
  • Landing Page Subtitle
  • Landing Page Content (rich text / HTML)
  • Hero Image (via Media Picker)
  • Success Title & Message (shown after form submission)
  • Expired Title & Message (shown when campaign is past end date)
  • Testimonials (add multiple: name, role, location, quote, rating, avatar via Media Picker)

Step 4: Event Details (for event-type campaigns)

  • Speakers: Name, Role, Bio, Talk Title, Photo (Media Picker), Social Links (Twitter, LinkedIn, Website)
  • Schedule: Multi-day agenda with sessions (time, title, description, speaker dropdown, location, type: keynote/panel/workshop/break/networking)
  • Partners: Name, Logo (Media Picker), Tier (title_sponsor/partner/supporter/media_partner), Website
  • Why Attend: Benefits list (title, description, icon)
  • Venue Info: Name, Address, City, Map URL, Directions
  • Live Stream: URL, Type (YouTube/Vimeo/Custom)

Step 5: Post-Event Content (for completed events)

  • Gallery: Multiple images via Media Picker with captions
  • Winners: Display mode (ranked/flat/categorized), Winner entries (name, award, category, rank, description, photo)
  • Recording URL: Link to event recording
  • Resources: Downloadable files (title, URL, type, description, thumbnail)

Step 6: Incentive

  • Incentive Type: none, free_trial, percentage_discount, fixed_discount
  • Incentive Value (e.g., 30 days trial, 20% off)
  • Incentive Description
  • Promo Code Prefix (for auto-generated codes)
  • Promo Validity Days
  • Linked Subscription Plan

Step 7: Review

  • Comprehensive preview of all configured sections
  • Click Create Campaign to save as draft

8.2 Edit Campaign

Navigate to Marketing → Campaigns → [Campaign Name] → Edit

All fields from the creation wizard are editable. Changes are saved immediately on form submission.

Important: Status changes are NOT allowed through the edit form. Use the Publish/Unpublish actions on the campaign detail page.

8.3 Campaign Detail Page (7 Tabs)

Navigate to Marketing → Campaigns → [Campaign Name]

TabDescription
DetailsCampaign info, form fields preview, incentive details, custom messages
SubmissionsSubmission management with approval workflow (see Section 10)
InvitationsEmail invitation management (see Section 11)
Layout & StylePage template picker and visual editor for campaign landing page
QR CodeAuto-generated QR code linking to campaign landing page URL
StatisticsCampaign metrics: total submissions, pending/approved/rejected counts, conversion rate
SocialLink to social campaign, compose social posts about the campaign

8.4 Layout & Style Tab

The Layout & Style tab integrates with the platform's page template system:

  • Select Template: Choose from available campaign-compatible templates
  • Configure Sections: Customize the order and content of campaign page sections
  • Save Draft: Save template changes without publishing
  • Publish: Push template changes live to the reader portal
  • Preview: Opens the reader portal campaign page in a new tab for visual review

Note: The preview requires the Reader Portal to be running. If preview shows "Preview Unavailable", verify NEXT_PUBLIC_READER_URL is correctly configured and the Reader Portal is running.

8.5 QR Code Tab

Generates a QR code linking to {READER_URL}/campaigns/{slug}. Can be downloaded for use in physical marketing materials.

8.6 Statistics Tab

Shows real-time campaign metrics:

  • Total Submissions
  • Pending Review Count
  • Approved Count
  • Rejected Count
  • Conversion Count (submissions that resulted in subscriptions)
  • Counts are sourced from actual submission records (not denormalized counters)

9. Sub-Module: Campaign Publishing & Landing Pages

Purpose

Control campaign visibility and manage the lifecycle of campaign landing pages accessible to the public.

Publishing Workflow

Publish a Campaign

  1. Navigate to the campaign detail page
  2. Ensure the campaign is in Draft or Paused status
  3. Click "Publish"
  4. System validates campaign completeness:
    • Name is set
    • Slug is set
    • Start date and end date are set
    • At least one form field is configured
  5. If validation passes: status changes to Active
  6. The campaign landing page is now live at {READER_URL}/campaigns/{slug}

Unpublish a Campaign

  1. From an Active campaign, click "Unpublish"
  2. Status changes to Paused
  3. The landing page shows a "Campaign Paused" message to visitors

Complete a Campaign

  1. From an Active campaign, click "Complete"
  2. Status changes to Completed
  3. The landing page shows an "Event Recap" or expired state with notify form

Archive a Campaign

  1. From a Completed or Paused campaign, click "Archive"
  2. Status changes to Archived
  3. Campaign is hidden from public access

Landing Page URL

Every campaign has a public URL: {READER_URL}/campaigns/{slug}

This URL is displayed prominently on the campaign detail page with a copy button.

Landing Page Behavior by Status

Campaign StatusLanding Page Behavior
DraftNot accessible (404)
Active (before start date)Shows "Upcoming" state with countdown, notify form
Active (within date range)Full campaign page with form, event content if applicable
Active (event-type, during event)"Happening Now" badge, live stream embed, schedule with "now" indicator
PausedShows "Campaign Paused" message
CompletedShows event recap: gallery, winners, resources, testimonials, notify form
ArchivedNot accessible (404)

Event-Type Campaign: 3-State Rendering

Event-type campaigns have enhanced rendering based on timing:

Upcoming State (before start date): Hero → Why Attend → Info → Countdown → Speakers → Schedule → Venue → Registration Form → Partners → Notify Form

Active State (during event): Hero (with "Happening Now") → Live Stream → Why Attend → Info → Schedule (with "now" indicator) → Speakers → Venue → Form (if capacity) → Partners → Testimonials

Completed State (after end date): Hero (recap) → Info → Gallery → Winners → Testimonials → Resources → Speakers → Partners → Notify Form (for next event)


10. Sub-Module: Campaign Submissions & Review

Purpose

Process and review form submissions from campaign landing pages, including approval workflows and promo code generation.

Access

Campaign Detail → Submissions tab

10.1 View Submissions

  • Table view with columns: Submitted At, Submitter Info, Status, Reviewed By, Actions
  • Filter by status: All / Pending Review / Approved / Rejected / Needs Info
  • Search submissions
  • Status count badges on each filter tab

10.2 Review a Submission

  1. Click on a submission row to expand details
  2. View all form field responses, uploaded files (selfie, ID card, documents)
  3. Choose an action:
ActionResult
ApproveStatus → approved. Optional: send approval email using campaign_submission_approved template
RejectStatus → rejected. Requires rejection reason. Optional: send rejection email with reason
Request InfoStatus → needs_info. Notifies submitter to provide additional information

10.3 Generate Promo Code

After approving a submission:

  1. Click "Generate Promo Code"
  2. System generates a unique code using the campaign's promoCodePrefix
  3. Code is linked to the submission (one-to-one)
  4. Code type, value, and validity are based on campaign incentive settings
  5. Approval email includes the generated promo code

10.4 Bulk Operations

  • Select multiple submissions → Bulk Approve or Bulk Reject
  • Requires MARKETING_SUBMISSIONS_REVIEW permission

10.5 Submission Data Flow

Reader submits form on landing page
  → POST `/api/marketing/campaigns/public/{slug}/submit`
  → Creates CampaignSubmission record
  → Increments campaign counters (totalSubmissions, pendingCount)
  → Upserts AudienceContact from form data (email, name, phone)
  → Admin reviews in Submissions tab
  → Approve → sends approval email → optionally generates promo code
  → Reject → sends rejection email with reason

11. Sub-Module: Campaign Invitations

Purpose

Send targeted email invitations to segmented audiences to drive campaign participation.

Access

Campaign Detail → Invitations tab

11.1 View Invitations

  • List of all invitations for the campaign
  • Status badges: Draft / Scheduled / Sending / Sent / Failed
  • Delivery metrics per invitation: Recipient Count, Delivered, Opened, Clicked, Registered

11.2 Create Invitation

  1. Click "New Invitation"
  2. Select Segment: Choose from existing segments (which now include audience contacts)
  3. View segment member count to confirm audience size
  4. Write Subject Line: Supports {{campaign_name}} variable
  5. Compose Email Content: Rich text with variable support:
    • {{user_name}} — Recipient name
    • {{campaign_name}} — Campaign name (auto-resolved)
    • {{campaign_url}} — Landing page URL with UTM parameters
    • {{incentive_description}} — Incentive details
  6. Preview: See rendered email with sample data
  7. Choose action:
    • Save as Draft — Save without sending
    • Send Now — Send immediately
    • Schedule — Set a future date/time for automated send

11.3 Send Invitation

  1. From a draft invitation, click "Send"
  2. System resolves segment members at send time
  3. Emails are dispatched using campaign_invitation template
  4. Campaign URL includes UTM parameters: ?utm_source=email&utm_medium=invitation&utm_campaign={slug}&source=email_invite
  5. Status transitions: draft → sending → sent (or failed)
  6. Recipient emails are snapshotted on the invitation record for audit

11.4 Scheduled Invitations

  • Scheduled invitations are picked up by the send-invitations cron job
  • Processed when scheduledAt <= now and status is scheduled

11.5 Template Variable Resolution

The invitation composer now correctly resolves {{campaign_name}} to the actual campaign name in both the slider header and email content display. This was fixed as part of the marketing feature review.


12. Sub-Module: Email Campaigns

Email campaigns
Create and send templated email campaigns to segmented audiences with engagement tracking.

Purpose

Send template-based or custom HTML email campaigns to segmented audiences with scheduling and engagement tracking.

Access

Admin Console → Marketing → Email Campaigns

12.1 View Email Campaigns

  • List with columns: Name, Subject, Segment, Status, Sent At, Open Rate, Click Rate
  • Status tabs: Draft / Scheduled / Sending / Sent / Failed
  • Count badges per tab

12.2 Create Email Campaign

  1. Click "New Email Campaign"
  2. Configure:
    • Name (required)
    • Subject Line (required, supports {{variable}} interpolation)
    • Email Template — select from existing templates OR provide custom HTML
    • Target Segment — select segment for audience targeting
  3. Save as draft

12.3 Send Email Campaign

Send Now:

  1. Open a draft email campaign
  2. Click "Send Now"
  3. System resolves segment members
  4. Enforces newsletter opt-in check (unless forceSend=true)
  5. Sends emails in batches via SendGrid/SMTP
  6. Status: draft → sending → sent

Schedule:

  1. Open a draft email campaign
  2. Click "Schedule"
  3. Set the scheduled date/time
  4. Status: draft → scheduled
  5. The send-scheduled-emails cron picks it up when due
  6. Cron atomically claims the campaign (scheduled → sending) to prevent double-send
  7. Status: sending → sent

12.4 Duplicate Campaign

  • Click "Duplicate" to create a copy as a new draft
  • Useful for recurring campaigns with slight modifications

12.5 Email Campaign Metrics

After sending, the campaign shows engagement metrics:

  • Delivered: Emails successfully delivered
  • Opened: Unique opens
  • Clicked: Unique link clicks
  • Bounced: Hard/soft bounces
  • Unsubscribed: Recipients who unsubscribed

Metrics are updated via SendGrid/Mailchimp engagement webhooks in real-time.

12.6 Delete Email Campaign

  • Only unsent campaigns (draft/scheduled) can be deleted
  • Sent/sending campaigns cannot be deleted (for audit compliance)

13. Sub-Module: Newsletter Editions

Purpose

Author newsletter editions in Strapi CMS, manage their lifecycle, send them as email campaigns, and provide a reader-facing archive.

Architecture Note

Newsletter editions use a dual-storage approach:

  • Strapi CMS: Source of truth for content authoring (rich text editing, cover images, SEO)
  • Admin Console API: Acts as proxy to Strapi, bridges to Prisma EmailCampaign for email delivery

Access

Admin Console → Marketing → Newsletters

13.1 View Newsletter Editions

  • List with status tabs: All / Draft / Published / Featured / Archived
  • Search by title
  • Columns: Title, Edition Number, Topic, Status, Publish Date

13.2 Create Newsletter Edition

  1. Click "New Newsletter"
  2. Fill in:
    • Title (required)
    • Edition Number (auto-incremented or manual)
    • Topic (for archive filtering)
    • Preview Text (teaser shown in archive cards)
    • Publish Date
    • Cover Image (via Media Picker)
    • HTML Content (rich text editor)
    • Plain Text Content (fallback for text-only email clients)
    • External Archive URL (optional, for linking to external archive)
  3. Save (creates in Strapi as draft)

13.3 Publish Newsletter

  1. Open a draft edition
  2. Click "Publish"
  3. Uses Strapi v5 publish action API
  4. Edition becomes visible in the public newsletter archive at {READER_URL}/newsletters

13.4 Send Newsletter as Email Campaign

  1. From the newsletters list, click "Send as Campaign" (or from edition detail page)
  2. A modal appears:
    • Select Segment — choose target audience
    • Subject Line — defaults to edition title
  3. Click Send
  4. System:
    • Fetches full content from Strapi
    • Creates a Prisma EmailCampaign with newsletterEditionId for traceability
    • Dispatches emails to segment members
  5. The email campaign appears in the Email Campaigns list for tracking

13.5 Reader Portal Newsletter Archive

  • Accessible at {READER_URL}/newsletters
  • Topic filter pills: Filter editions by topic
  • Edition cards: Show edition number, featured badge, topic tag, title, publish date, preview text
  • Pagination: Navigate through archive
  • Individual edition page: {READER_URL}/newsletters/{slug} — renders full HTML content
  • ISR: Archive page uses Incremental Static Regeneration with 5-minute revalidation

14. Sub-Module: Promotional Codes

Promotional codes
Manage promotional codes with usage limits, expiration dates, and discount configurations.

Purpose

Create and manage discount codes for subscription plans. Promo codes can be admin-created (DiscountCoupon) or auto-generated from campaign submissions (PromotionalCode).

Access

Admin Console → Marketing → Promo Codes

14.1 Two Types of Promo Codes

TypeModelSourcePurpose
Admin-Created CouponsDiscountCouponCreated manually by adminsReusable discount codes (e.g., WELCOME20)
Campaign-Generated CodesPromotionalCodeAuto-generated from approved campaign submissionsOne-time codes tied to individual submissions

14.2 Create Admin Coupon

  1. Click "New Promo Code"
  2. Configure:
    • Code (unique, e.g., SUMMER2026)
    • Description
    • Discount Type: Percentage or Fixed Amount
    • Discount Value (e.g., 20% or $10)
    • Linked Plan (optional, restrict to specific subscription plan)
    • Billing Interval (optional)
    • Max Uses (total redemption limit)
    • Max Uses Per User
    • Valid From / Valid To dates
    • Options: New subscribers only, applies to (FIRST_PAYMENT / N_CYCLES / ALL_RENEWALS), discount cycles, allow gift/bulk/institutional
  3. Save

14.3 Campaign Promo Code Auto-Generation

When a campaign submission is approved and the campaign has incentive settings:

  1. Admin approves submission → clicks "Generate Promo Code"
  2. System generates a unique code: {promoCodePrefix}-{random}
  3. Code is saved as PromotionalCode with:
    • Type: percentage, fixed, or trial_days (from campaign incentive settings)
    • Value: from campaign incentiveValue
    • Validity: from campaign promoValidityDays
    • Max redemptions: 1 (single use)
  4. Code is linked to the submission record

14.4 Public Promo Code Validation & Redemption

Validate (POST /api/promo-codes/validate or POST /api/promo/validate):

  • Checks both PromotionalCode and DiscountCoupon tables
  • Returns discount type, value, validity
  • Rate limited: 20 requests/minute/IP

Redeem (POST /api/promo/redeem):

  • For trial_days: Atomically creates reader account + subscription in a transaction
  • For percentage/fixed: Atomically claims redemption slot
  • Rate limited: 5 requests/15 minutes/IP

15. Sub-Module: Conversion Surfaces

Conversion surfaces
Configure modals, banners, slide-ins, and inline prompts to convert readers into subscribers.

Purpose

Deploy promotional overlays (modals, banners, slide-ins, sticky notifications) on the Reader Portal with sophisticated targeting and trigger rules to drive reader actions.

Access

Admin Console → Marketing → Conversion Surfaces

15.1 Surface Types

TypeBehavior
modalFull-screen overlay with backdrop
sticky_notificationPersistent notification bar
slide_inCorner slide-in panel
top_bannerFull-width banner at page top
bottom_bannerFull-width banner at page bottom
inline_embedEmbedded within page content

15.2 Create Conversion Surface

  1. Click "New Surface"
  2. Configure:

Identity & Presentation:

  • Name, Surface Type, Placement
  • Title, Subtitle, Body Text
  • Image URL
  • Primary CTA (text + URL), Secondary CTA (text + URL)
  • Background Color, Theme (light/dark/brand)

Conversion Goal:

  • register, login, subscribe_paid, subscribe_newsletter, view_campaign, view_event, redeem_coupon, view_product, visit_pricing, custom_cta

Entity Links (optional):

  • Linked Coupon (DiscountCoupon)
  • Linked Plan (SubscriptionPlan)
  • Linked Campaign (OutreachCampaign)
  • Linked Event
  • Linked Product
  • Newsletter Slug

Targeting Rules (JSON):

  • audiences: anonymous, registered, subscriber, newsletter_subscribed, newsletter_unsubscribed
  • pageTypes: homepage, article, section, etc.
  • deviceTypes: mobile, tablet, desktop
  • includeUrls / excludeUrls: URL pattern matching
  • contentTypes, sections, tags: Content-based targeting
  • utmSource, utmMedium, utmCampaign: UTM parameter matching

Trigger Configuration:

  • page_load — Show on page load (with optional delay)
  • time_on_page — Show after N seconds
  • scroll_depth — Show after scrolling to N%
  • exit_intent — Show when mouse leaves viewport
  • page_view_count — Show after N page views
  • after_article_read — Show after scrolling to 100% of article
  • manual — Programmatic trigger only

Frequency & Suppression:

  • Frequency Limit + Unit (per session/day/week/month)
  • Suppress After Dismiss (boolean)
  • Suppress After Conversion (boolean)
  • Suppress If Already Converted (boolean)

Scheduling:

  • Status: draft, scheduled, active, paused
  • Starts At / Ends At dates
  • Priority (lower number = higher priority)
  1. Save as draft

15.3 Activate Surface

  1. Change status from draftactive (via PUT API)
  2. Surface immediately begins displaying to eligible readers on the Reader Portal

15.4 Reader Portal Rendering

The conversion surface system uses a layered rendering architecture:

  1. GlobalConversionSurfaces — Wraps the entire Reader Portal root layout
  2. ConversionSurfaceProvider — React context that:
    • Detects device type (mobile/tablet/desktop)
    • Counts page views (stored in state, incremented per mount)
    • Fetches eligible surfaces from GET /api/public/conversion-surfaces/eligible
    • Filters through local suppression checks (localStorage for anonymous, server-side for authenticated)
    • Sorts by priority
    • Enforces slot-based conflict resolution: one modal at a time, one sticky at a time, banners can coexist
  3. ConversionSurfaceRenderer — Evaluates trigger conditions and renders the appropriate surface component

Trigger Evaluation:

  • page_load: Fires after optional triggerDelaySeconds
  • time_on_page: Timer starts on mount, fires at triggerDelaySeconds
  • scroll_depth: Monitors scroll position, fires when triggerScrollPercent is reached
  • exit_intent: Listens for mouseout event leaving the viewport (desktop only)
  • page_view_count: Compares provider's page view count against triggerPageViews
  • after_article_read: Monitors scroll to 100% of content
  • manual: Not automatically triggered

Frequency Enforcement:

  • Anonymous readers: Client-side via localStorage (per-surface tracking)
  • Authenticated readers: Server-side via ConversionEvent records

15.5 Analytics

Dual tracking approach:

  1. Internal (ConversionEvent table): Stores every interaction with reader context (page, device, UTM, session)
  2. GA4: Fires custom events for cross-platform attribution

Stats API (GET /api/marketing/conversion-surfaces/{id}/stats):

  • Date range filtering
  • Time series data
  • Breakdowns by device, page, UTM
  • Computed rates: CTR, conversion rate, dismiss rate, engagement rate

Denormalized counters on ConversionSurface model:

  • impressionCount, clickCount, dismissCount, conversionCount
  • Updated atomically on each event
  • Used for fast list view display

15.6 Coupon Integration

Surfaces can link to DiscountCoupon records:

  • Eligibility API validates coupon status (active, not expired, usage limit not reached)
  • Reader Portal shows copyable coupon code with "Copy Code" button
  • coupon_copied event tracked to both GA4 and internal analytics
  • Coupon code passed to checkout URL as parameter

16. Sub-Module: Campaign Types

Purpose

Define and manage reusable campaign type categories that appear in the campaign creation dropdown.

Access

Admin Console → Marketing → Campaign Types

Features

  • CRUD: Create, read, update, delete campaign types
  • Fields: Name (unique), Slug (unique, auto-generated), Description, Icon (Lucide icon name), Active flag, Sort Order
  • Seeded Defaults: 5 types seeded from the system enum: Student Outreach, Event Registration, Lead Generation, Institutional, General
  • Usage: Campaign types appear in the campaign creation wizard and campaign edit form as a dropdown
  • Deletion Protection: Cannot delete a type that is linked to existing campaigns

17. Cron Jobs & Automation

Overview

Cron EndpointPurposeAuthRecommended Schedule
GET /api/cron/send-scheduled-emailsSend due email campaignsCRON_SECRETEvery 1–5 min
GET /api/cron/campaign-statusAuto-complete expired campaigns; send notify emailsCRON_SECRETEvery 15–30 min
GET /api/cron/send-invitationsSend scheduled campaign invitationsCRON_SECRETEvery 5 min
GET /api/cron/expire-invitationsExpire overdue invitationsCRON_SECRETEvery hour
GET /api/cron/invitation-remindersWeekly reminders for pending invitationsCRON_SECRETWeekly

send-scheduled-emails

  1. Finds all email campaigns with status = 'scheduled' and scheduledAt <= now
  2. Atomically transitions status to 'sending' (prevents double-send on concurrent cron runs)
  3. Resolves segment members
  4. Sends emails via SendGrid/SMTP
  5. Updates status to 'sent' or 'failed'

campaign-status

  1. Finds active campaigns where endDate < now → transitions to 'completed'
  2. Finds draft campaigns where endDate < now → transitions to 'archived'
  3. Looks up CampaignNotifyRequest records for campaigns that just became active → sends notification emails using campaign_notify_new_campaign template

send-invitations

  1. Finds invitations with status = 'scheduled' and scheduledAt <= now
  2. Resolves segment members for each invitation
  3. Sends invitation emails
  4. Updates status to 'sent' or 'failed'

18. Webhook Integrations

SendGrid Engagement Webhook

Endpoint: POST /api/webhooks/sendgrid

Events Processed:

EventAction
deliveredUpdates EmailLog status, increments campaign delivered count
openIncrements campaign opened count
clickIncrements campaign clicked count
bounceUpdates EmailLog status to bounced, increments bounced count
droppedLogs delivery failure
spamreportLogs spam report
unsubscribeIncrements unsubscribed count

Authentication: ECDSA P-256 signature verification (optional, controlled by SENDGRID_WEBHOOK_VERIFICATION_KEY)

Mailchimp/Mandrill Engagement Webhook

Endpoint: POST /api/webhooks/mailchimp

Events Processed: send, open, click, hard_bounce, soft_bounce, spam, unsub

Authentication: HMAC-SHA1 signature verification (optional, controlled by MAILCHIMP_WEBHOOK_KEY)

URL Verification: GET /api/webhooks/mailchimp returns 200 OK for Mandrill webhook registration.


19. Reader Portal Rendering

Campaign Landing Pages

URL: {READER_URL}/campaigns/{slug}

Technology: Next.js App Router, Server Component with ISR (60-second revalidation)

Components (24 campaign section components):

ComponentPurpose
CampaignHeroHero image with title/subtitle, status badges, date range
CampaignInfoRich content rendering (HTML/description)
CampaignFormDynamic form builder with 12 field types (text, email, phone, select, radio, checkbox, textarea, date, institution_picker, photo_selfie, photo_id_card, file_upload), validation, file upload
CampaignCtaCall-to-action with incentive info (state-aware: hidden for expired/paused/completed)
CampaignSuccessPost-submission confirmation with share/copy link
CampaignExpiredExpired state with messaging and notify form
CampaignUpcomingPre-start date state with countdown and notify form
CampaignPausedPaused state with messaging and notify form
CampaignCountdownReal-time countdown timer to start/end
CampaignSpeakersSpeaker grid with photos, bios, social links (Twitter/LinkedIn/Website)
CampaignScheduleMulti-day agenda with session types, "Happening Now" indicator, ICS calendar download
CampaignVenueVenue details with Google Maps embed iframe, address, city, directions
CampaignWhyAttendFeature/benefit highlights grid with emoji icons
CampaignPartnersPartner logos grouped by tier (Title Sponsor, Partners, Supporters, Media Partners)
CampaignLiveStreamLive stream embed (YouTube/Vimeo/custom) with "LIVE NOW" / "Recording" badge
CampaignGalleryPost-event photo gallery with lightbox viewer and keyboard navigation
CampaignWinnersAwards display (ranked podium / flat grid / categorized groups)
CampaignResourcesDownloadable resources with type badges (video/article/document)
CampaignTestimonialTestimonial cards with quotes, ratings, and avatars
CampaignNotifyFormReusable "Get Notified" form for expired/upcoming/paused campaigns
CampaignNotifyFormSectionWrapper for notify form as standalone section
CampaignProgramCalendarMulti-track or timeline calendar view of event schedule
CampaignListingGridGrid of campaign cards with status badges for campaign listing pages
CampaignListingHeroHero section for campaign listing pages

Newsletter Archive

URL: {READER_URL}/newsletters

Components:

  • NewsletterArchive — Topic filter pills, paginated edition grid
  • NewsletterCard — Edition card with number badge, topic, date, preview
  • NewsletterArchiveHeader — Page header
  • Individual edition: {READER_URL}/newsletters/{slug}

Programs Discovery Page

URL: {READER_URL}/programs

Features:

  • Browse past/ongoing/upcoming campaigns
  • Filter by campaign type, status, institute, city, country
  • Card grid with status badges, date ranges, type badges
  • Links to campaign landing pages

Conversion Surfaces

Renders globally across all Reader Portal pages via GlobalConversionSurfaces in the root layout.

See Section 15.4 for detailed rendering behavior.


20. Business Logic & Rules

Campaign Validation Rules

  • Campaign slug must be unique across all campaigns
  • Start date must be before end date
  • At least one form field required before publishing
  • Status transitions follow a strict state machine (see Section 4)
  • Cannot delete a campaign that has submissions (use archive instead)

Segment Resolution Rules

  • Filters use AND logic between groups, OR logic within a group
  • When source includes "audience contacts", queries both Reader and AudienceContact tables
  • Members are resolved dynamically at query time (not cached lists)
  • Segment cannot be deleted if used by active campaigns or invitations

Email Campaign Rules

  • Cannot update or delete sent/sending campaigns
  • Scheduled campaigns are atomically claimed by cron to prevent double-send
  • Newsletter opt-in is enforced by default (unless forceSend=true)
  • Template variables use Handlebars syntax: {{variable_name}}

Conversion Surface Rules

  • Slot-based conflict resolution: Maximum one modal, one sticky notification active simultaneously; banners can coexist
  • Priority: Lower number = higher priority; when multiple surfaces compete for a slot, highest priority wins
  • Frequency capping: Configurable per session/day/week/month
  • Suppression: Can suppress after dismiss, after conversion, or if already converted
  • Targeting: AND between dimension types, OR within a dimension
  • Soft delete: Archive instead of hard delete (preserves analytics history)

Promotional Code Rules

  • Campaign-generated codes are single-use (maxRedemptions = 1)
  • Admin coupons can have configurable max uses and per-user limits
  • Validation checks: code exists, is active, not expired, usage limit not reached
  • trial_days redemption atomically creates reader account + subscription
  • Rate limiting on public validation (20/min) and redemption (5/15min)

Contact/Audience Rules

  • Email must be unique across audience contacts
  • Inactive contacts (isActive = false) are excluded from segments
  • Unsubscribed contacts are excluded from non-forced email sends
  • Auto-upsert from campaign submissions and newsletter signups is non-blocking (failures don't break primary flow)

21. End-to-End User Flows

Flow 1: Create and Launch an Outreach Campaign

1. Create Campaign Type (if needed)
   Marketing → Campaign Types → Add → "Conference"

2. Build Audience Segment
   Marketing → Segments → New Segment
   → Filter: Account Status = Active, Subscription = None
   → Save as "Non-Subscribers"

3. Create Campaign
   Marketing → Campaigns → New Campaign
   → Step 1: Name, Slug, Type, Dates
   → Step 2: Form Fields (Name, Email, Phone, Institution)
   → Step 3: Landing Page (Hero, Content, Success/Expired messages)
   → Step 4: Event Details (Speakers, Schedule, Venue) — if event
   → Step 5: Post-Event Content — skip for now
   → Step 6: Incentive (Free Trial, 30 days)
   → Step 7: Review → Create

4. Configure Landing Page Template
   Campaign Detail → Layout & Style tab
   → Select template → Save

5. Publish Campaign
   Campaign Detail → Click "Publish"
   → Validates → Status = Active
   → Copy landing page URL

6. Send Invitations
   Campaign Detail → Invitations tab → New Invitation
   → Select "Non-Subscribers" segment
   → Compose email → Send

7. Share on Social
   Campaign Detail → Social tab
   → "Create Social Campaign" → "Compose Post"
   → Pre-filled with campaign name/URL → Publish

8. Monitor
   Campaign Detail → Statistics tab → View submissions count
   → Submissions tab → Review & approve submissions
   → Generate promo codes for approved submissions

9. Complete Campaign
   After end date: Click "Complete"
   Add post-event content (gallery, winners, resources)
   Campaign page shows recap state

Flow 2: Newsletter Edition → Email Campaign

1. Create Newsletter Edition
   Marketing → Newsletters → New Newsletter
   → Author content in form
   → Save as draft

2. Publish to Archive
   Newsletter detail → Click "Publish"
   → Visible at `/newsletters/{slug}`

3. Send as Email Campaign
   Newsletters list → "Send as Campaign" button
   → Select target segment
   → Confirm subject line
   → Send

4. Track Engagement
   Marketing → Email Campaigns
   → Find campaign → View open/click/bounce metrics

Flow 3: Deploy a Conversion Surface

1. Create Coupon (optional)
   Marketing → Promo Codes → New Code
   → Code: "SAVE20", 20% off

2. Create Conversion Surface
   Marketing → Conversion Surfaces → New Surface
   → Type: Modal, Theme: Brand
   → Title: "Subscribe & Save 20%"
   → Link coupon: "SAVE20"
   → Goal: subscribe_paid
   → Targeting: anonymous + homepage + desktop
   → Trigger: exit_intent
   → Frequency: 1 per week
   → Suppress after conversion

3. Activate
   Set status to "active"
   → Surface appears to matching readers on Reader Portal

4. Monitor
   Conversion Surfaces list → View impression/click/conversion metrics
   → Detail → Stats → Time series, breakdowns

Internal — QA, Testing & Limitations
22. QA Scenarios & Test Matrix
Test Preconditions
  • Email provider configured and verified (SMTP status = configured)
  • Email templates seeded (4 campaign templates exist)
  • Email preferences/branding configured
  • RBAC permissions assigned to test role
  • Cron secret configured
  • Reader Portal running and accessible
  • S3 storage configured (for file upload tests)
  • At least one subscription plan exists (for promo code tests)
Scenario Matrix
A. Audience Contacts
#ScenarioStepsExpected Result
A1Add single contactMarketing → Segments → Contacts → Add Contact → Fill email + name → SaveContact appears in table
A2CSV upload — happy pathUpload valid CSV with email, name, phone columnsCreated count matches, contacts in table
A3CSV upload — duplicate emailsUpload CSV with existing emailsUpdated count > 0, no duplicates
A4CSV upload — missing email columnUpload CSV without email columnError: email column required
A5Edit contactClick contact → Edit fields → SaveFields updated
A6Delete contactSelect contact → DeleteContact removed from table (soft delete)
A7Export contactsClick ExportCSV downloads with all active contacts
A8Auto-create from campaign submissionSubmit campaign form with emailNew contact appears with tag campaign_submission
A9Auto-create from newsletter signupSubscribe to newsletterNew contact appears with tag newsletter_signup
A10Tag filteringAdd tags to contacts → Filter by tagOnly matching contacts shown
B. User Segments
#ScenarioStepsExpected Result
B1Create segment — readers onlyNew Segment → Source: Readers → Subscription: Active → SaveMember count shows active subscribers
B2Create segment — audience onlyNew Segment → Source: Audience Contacts → Tags: "campaign" → SaveMember count shows tagged contacts
B3Create segment — mixed sourceSource: All → SaveCount includes both readers and contacts
B4Live member count previewChange filters → Watch count updateCount updates within 500ms debounce
B5Preview membersClick Preview MembersPaginated list with source labels
B6Export segmentClick ExportCSV with source column
B7Delete used segmentTry deleting segment used by active invitationError: segment in use
B8Delete unused segmentDelete segment with no referencesSegment removed
B9Empty segmentCreate segment with no matching filtersCount = 0, preview shows empty state
C. Outreach Campaigns
#ScenarioStepsExpected Result
C1Create campaign — full wizardComplete all 7 steps → CreateCampaign saved as draft
C2Create campaign — minimalOnly required fields (name, slug, dates, 1 form field) → CreateCampaign saved
C3Duplicate slugCreate campaign with existing slugError: slug must be unique
C4Edit campaignEdit → Change fields → SaveFields updated
C5Publish campaignDraft → PublishStatus = active, landing page accessible
C6Publish incompleteDraft without form fields → PublishValidation error
C7Unpublish campaignActive → UnpublishStatus = paused, landing page shows paused
C8Complete campaignActive → CompleteStatus = completed
C9Archive campaignCompleted → ArchiveStatus = archived, landing page 404
C10Delete with submissionsTry deleting campaign with submissionsError: use archive instead
C11Delete without submissionsDelete draft campaign with no submissionsCampaign removed
D. Campaign Landing Pages (Reader Portal)
#ScenarioStepsExpected Result
D1Active campaign pageVisit /campaigns/{slug} for active campaignFull landing page renders
D2Expired campaign pageVisit page for completed campaignExpired state with recap content
D3Upcoming campaign pageVisit page for campaign with future start dateUpcoming state with countdown
D4Paused campaign pageVisit page for paused campaignPaused message shown
D5Draft/archived pageVisit page for draft/archived campaign404 not found
D6Form submission — happy pathFill all required fields → SubmitSuccess message, submission created
D7Form submission — validationSubmit with empty required fieldsValidation errors shown per field
D8Form — file uploadSubmit form with file_upload fieldFile uploaded to S3, shown in submission
D9Form — photo selfieUse selfie field on mobileCamera option available, photo uploaded
D10Form — institution pickerType institution nameAutocomplete suggestions appear
D11Notify form (expired)Submit notify form on expired campaignNotifyRequest created
D12Event page — speakersVisit event campaign with speakers dataSpeaker grid with photos and links
D13Event page — scheduleVisit event with scheduleMulti-day agenda renders
D14Event page — countdownVisit upcoming eventReal-time countdown timer ticks
D15Event page — live streamVisit active event with live stream URLStream embed renders
D16Event page — post-eventVisit completed event with gallery/winnersGallery and winners render
E. Submissions & Review
#ScenarioStepsExpected Result
E1View submissionsCampaign Detail → Submissions tabList of submissions with status
E2Approve submissionSelect submission → ApproveStatus = approved, approval email sent
E3Reject submissionSelect → Reject → Enter reasonStatus = rejected, rejection email sent
E4Request infoSelect → Request InfoStatus = needs_info
E5Generate promo codeApprove → Generate Promo CodeUnique code generated, linked to submission
E6Bulk approveSelect multiple → Bulk ApproveAll selected approved
E7Bulk rejectSelect multiple → Bulk RejectAll selected rejected
E8Permission checkUser without REVIEW permission → Try approveAction blocked / hidden
F. Email Campaigns
#ScenarioStepsExpected Result
F1Create email campaignNew → Name, Subject, Template, Segment → SaveDraft campaign created
F2Send nowDraft → Send NowStatus: draft → sending → sent
F3Schedule sendDraft → Schedule → Set future timeStatus = scheduled
F4Scheduled deliveryWait for cron trigger after scheduled timeStatus: scheduled → sending → sent
F5Duplicate campaignClick DuplicateNew draft created with same content
F6Delete draftDelete draft campaignCampaign removed
F7Delete sent campaignTry deleting sent campaignError: cannot delete sent campaigns
F8Engagement trackingSend campaign → Open email → Click linkMetrics update (delivered, opened, clicked)
F9Newsletter opt-in enforcementSend to segment with non-opted-in membersNon-opted-in members skipped (unless forceSend)
G. Conversion Surfaces
#ScenarioStepsExpected Result
G1Create modal surfaceNew → Type: Modal → Configure → SaveSurface saved as draft
G2Activate surfaceSet status to activeSurface visible to matching readers
G3Exit intent triggerSet trigger: exit_intent → Move mouse out of viewportModal appears
G4Scroll depth triggerSet trigger: scroll_depth 50% → Scroll to 50%Surface appears
G5Time on page triggerSet trigger: time_on_page 10s → Wait 10sSurface appears
G6Frequency cappingSet frequency: 1/week → Trigger → Dismiss → RevisitSurface does not reappear
G7Targeting — anonymous onlyTarget: anonymous → Visit as logged-in userSurface does NOT appear
G8Targeting — homepage onlyTarget: pageType: homepage → Visit article pageSurface does NOT appear
G9Coupon copySurface with linked coupon → Click "Copy Code"Code copied to clipboard, event tracked
G10Conflict resolutionCreate 2 active modals → Visit matching pageOnly higher-priority modal shows
G11Suppress after dismissEnable suppress → Dismiss surface → RevisitSurface does not reappear
G12Analytics trackingTrigger surface → InteractEvents recorded in ConversionEvent + GA4
H. Cron Jobs
#ScenarioStepsExpected Result
H1Cron authCall cron endpoint without CRON_SECRET401 Unauthorized
H2Campaign auto-completeActive campaign past end date → Trigger cronStatus → completed
H3Scheduled email deliverySchedule email for past time → Trigger cronEmail sent, status → sent
H4Double-send preventionTrigger send-scheduled-emails twice concurrentlyOnly one run processes the campaign
H5Invitation send via cronSchedule invitation → Trigger cronInvitation emails sent
I. Permissions / RBAC
#ScenarioStepsExpected Result
I1No marketing readUser without MARKETING_CAMPAIGNS_READ → Visit /marketingCampaigns card hidden
I2Read onlyUser with READ but not CREATE → Try creating campaignCreate button hidden / action blocked
I3No send permissionUser without EMAIL_CAMPAIGNS_SEND → Try sendingSend button hidden / action blocked
I4Full marketing adminUser with all marketing permissionsFull access to all features

23. Troubleshooting & Common Issues
Issue: Emails Not Being Sent

Symptoms: Email campaigns stuck in "sending", invitation emails not delivered

Check:

  1. SMTP/SendGrid configuration: Admin → Settings → Email → SMTP Status
  2. Verify SENDGRID_API_KEY or SMTP credentials in environment
  3. Check email logs for delivery errors: Query EmailLog for status = 'failed'
  4. Verify cron jobs are running (for scheduled sends)
Issue: Campaign Landing Page Shows 404

Symptoms: Published campaign not accessible at /campaigns/{slug}

Check:

  1. Campaign status is active (not draft or archived)
  2. Campaign dates: Start date must be in the past for active state
  3. Reader Portal is running
  4. NEXT_PUBLIC_ADMIN_API_URL is correctly configured in Reader Portal
  5. Campaign slug matches the URL exactly
Issue: Conversion Surface Not Appearing

Symptoms: Active surface not showing on Reader Portal

Check:

  1. Surface status is active
  2. Targeting rules match the current page/device/user state
  3. Frequency cap not exceeded (clear localStorage for anonymous testing)
  4. Not suppressed by dismiss/conversion rules
  5. Not blocked by conflict resolution (another higher-priority surface using the slot)
  6. Trigger conditions met (e.g., scroll depth reached, time elapsed)
Issue: Campaign Preview Shows "Preview Unavailable"

Symptoms: Layout & Style tab shows iframe error

Check:

  1. Reader Portal is running on the expected port
  2. NEXT_PUBLIC_READER_URL is correctly set in Admin Console environment
  3. No module boundary errors in Reader Portal (check console logs)
Issue: Segment Shows 0 Members

Symptoms: Segment with filters shows no matching members

Check:

  1. Contact Source filter: Ensure correct source is selected (Readers vs Audience vs All)
  2. Filter combination may be too restrictive (AND logic between groups)
  3. For audience contacts: Ensure contacts are active (isActive = true)
  4. For readers: Ensure matching subscription/status criteria exist
Issue: Newsletter Editions Not Loading

Symptoms: Newsletters page shows empty or errors

Check:

  1. Strapi is running and accessible
  2. STRAPI_URL and STRAPI_API_TOKEN are configured
  3. Newsletter edition content type exists in Strapi
  4. Editions have been published in Strapi (draft editions only show in admin)
Issue: Webhook Metrics Not Updating

Symptoms: Email campaign shows 0 opens/clicks after sending

Check:

  1. Webhook URL registered in SendGrid/Mailchimp dashboard
  2. Webhook endpoint is publicly accessible
  3. Check webhook signature verification (disable temporarily for debugging)
  4. Verify X-SMTPAPI unique_args are being set at send time
Issue: Promo Code Redemption Fails

Symptoms: Valid-looking code returns error on redemption

Check:

  1. Code exists and isActive = true
  2. Code not expired (validUntil > now)
  3. Usage limit not reached (currentRedemptions < maxRedemptions)
  4. For campaign codes: Submission must be approved first
  5. Rate limiting: Max 5 redemptions per 15 minutes per IP

24. Known Limitations & Current Gaps
Planned but Not Yet Implemented
FeatureStatusNotes
A/B Testing for Conversion SurfacesPlannedRun multiple variants with statistical significance tracking
Geographic TargetingPlannedTarget conversion surfaces by reader location
Webhook Events for Campaign LifecyclePlannedEvents: created, activated, conversion, milestone
Automated Conversion Surface Expiration CronPlannedAuto-transition scheduled → active → expired
Admin Preview for Conversion SurfacesPlannedPreview surface rendering before activation
Bulk Operations for Conversion SurfacesPlannedBulk pause, activate, archive
Template Library for SurfacesPlannedPre-built templates for common patterns
Content-Aware Inline Embed PositioningPlannedAuto-position inline embeds based on article structure
Current Limitations
  1. No real-time websocket notifications — Notification bell uses polling, not push
  2. Newsletter content authoring requires Strapi — No built-in rich text editor for newsletters in Admin Console
  3. Conversion surface preview — Cannot preview a surface's appearance in Admin Console before activating
  4. Campaign form field types — No conditional logic (show/hide fields based on other field values)
  5. Segment caching — Segments resolve dynamically on every query; large segments may have latency
  6. Email campaign retry — Failed campaigns must be manually retried (no automatic retry)
  7. Invitation tracking — Individual recipient open/click tracking depends on email provider webhook reliability
  8. Conversion surface inline embed — Requires manual template configuration; no automatic content-aware positioning

25. Appendix
A. API Endpoint Reference
Admin Marketing APIs (Base: /api/marketing)
EndpointMethodsAuthSection
/campaignsGET, POSTSession + RBACCampaigns
/campaigns/[id]GET, PUT, DELETESession + RBACCampaigns
/campaigns/[id]/publishPOSTSession + RBACPublishing
/campaigns/[id]/statsGETSession + RBACStatistics
/campaigns/[id]/qr-codeGETSession + RBACQR Code
/campaigns/[id]/submissionsGET, POSTSession + RBACSubmissions
/campaigns/[id]/submissions/[submissionId]GET, PUTSession + RBACSubmissions
/campaigns/[id]/submissions/[submissionId]/generate-promoPOSTSession + RBACPromo Codes
/campaigns/[id]/submissions/bulkPOSTSession + RBACSubmissions
/campaigns/[id]/invitationsGET, POSTSession + RBACInvitations
/campaigns/[id]/invitations/[invitationId]GET, DELETESession + RBACInvitations
/campaigns/[id]/invitations/[invitationId]/sendPOSTSession + RBACInvitations
/campaigns/[id]/institutesGET, POSTSession + RBACInstitutes
/campaigns/[id]/social-campaignGET, POSTSession + RBACSocial
/campaigns/public/[slug]GETNonePublic Landing Page
/campaigns/public/[slug]/submitPOSTNonePublic Form Submit
/campaigns/public/[slug]/notifyPOSTNonePublic Notify
/campaigns/public/[slug]/uploadPOSTNonePublic File Upload
/campaigns/public/reference-dataGETNonePublic Reference Data
/campaigns/public/institutionsGETNonePublic Institution Search
/campaigns/public/programsGETNonePublic Programs Listing
/email-campaignsGET, POSTSession + RBACEmail Campaigns
/email-campaigns/[id]GET, PUT, DELETESession + RBACEmail Campaigns
/email-campaigns/[id]/sendPOSTSession + RBACEmail Send
/email-campaigns/[id]/previewPOSTSession + RBACEmail Preview
/email-campaigns/[id]/duplicatePOSTSession + RBACEmail Duplicate
/segmentsGET, POSTSession + RBACSegments
/segments/[id]GET, PUT, DELETESession + RBACSegments
/segments/[id]/previewGETSession + RBACSegment Preview
/segments/[id]/exportGETSession + RBACSegment Export
/segments/previewPOSTSession + RBACPreview w/o Save
/audienceGET, POSTSession + RBACAudience Contacts
/audience/[id]GET, PUT, DELETESession + RBACAudience Contacts
/audience/uploadPOSTSession + RBACCSV Upload
/audience/exportGETSession + RBACCSV Export
/promo-codesGET, POSTSession + RBACPromo Codes
/promo-codes/[id]GET, PUT, DELETESession + RBACPromo Codes
/promo-codes/validatePOSTSession + RBACValidate Code
/newslettersGET, POSTSession + RBACNewsletters
/newsletters/[documentId]GET, PUT, DELETESession + RBACNewsletters
/newsletters/[documentId]/publishPOSTSession + RBACNewsletter Publish
/newsletters/[documentId]/sendPOSTSession + RBACNewsletter Send
/newsletters/publicGETNonePublic Archive
/newsletters/public/[slug]GETNonePublic Edition
/conversion-surfacesGET, POSTSession + RBACConversion Surfaces
/conversion-surfaces/[id]GET, PUT, DELETESession + RBACConversion Surfaces
/conversion-surfaces/[id]/statsGETSession + RBACSurface Analytics
/conversion-surfaces/[id]/duplicatePOSTSession + RBACSurface Duplicate
/campaign-typesGET, POSTSession + RBACCampaign Types
/campaign-types/[id]GET, PUT, DELETESession + RBACCampaign Types
Public APIs
EndpointMethodsPurpose
/api/public/conversion-surfaces/eligibleGETFetch eligible surfaces for reader context
/api/public/conversion-surfaces/eventPOSTTrack conversion surface events
/api/promo/validatePOSTValidate promo code (rate limited)
/api/promo/redeemPOSTRedeem promo code (rate limited)
B. Key Enums Reference
EnumValues
OutreachCampaignStatusdraft, active, paused, completed, archived
OutreachCampaignTypestudent_outreach, event, lead_gen, institutional, general
CampaignSubmissionStatuspending_review, approved, rejected, needs_info
CampaignInvitationStatusdraft, scheduled, sending, sent, failed
EmailCampaignStatusdraft, scheduled, sending, sent, failed
PromoCodeTypepercentage, fixed, trial_days
ConversionSurfaceTypemodal, sticky_notification, slide_in, top_banner, bottom_banner, inline_embed
ConversionSurfaceStatusdraft, scheduled, active, paused, expired, archived
ConversionGoalregister, login, subscribe_paid, subscribe_newsletter, view_campaign, view_event, redeem_coupon, view_product, visit_pricing, custom_cta
ConversionTriggerpage_load, time_on_page, scroll_depth, exit_intent, page_view_count, after_article_read, manual
ConversionEventTypeimpression, click, dismiss, conversion, coupon_copied
FrequencyUnitsession, day, week, month
NewsletterEditionStatusdraft, published, featured, archived
C. Email Template Keys
Template KeyVariablesUsed By
campaign_submission_approveduser_name, campaign_name, promo_code, incentive_descriptionSubmission approval
campaign_submission_rejecteduser_name, campaign_name, rejection_reasonSubmission rejection
campaign_invitationuser_name, campaign_name, campaign_url, incentive_descriptionCampaign invitations
campaign_notify_new_campaignuser_name, campaign_name, campaign_urlNotify requests
D. Environment Variables Summary
VariableRequired ForUsed By
SENDGRID_API_KEYEmail delivery (SendGrid)Admin Console
SMTP_HOST/PORT/USER/PASSEmail delivery (SMTP)Admin Console
EMAIL_FROM_ADDRESSAll outgoing emailsAdmin Console
EMAIL_FROM_NAMEAll outgoing emailsAdmin Console
STRAPI_URLNewsletter editionsAdmin Console
STRAPI_API_TOKENNewsletter editionsAdmin Console
AWS_S3_BUCKETFile uploadsAdmin Console
AWS_ACCESS_KEY_IDFile uploadsAdmin Console
AWS_SECRET_ACCESS_KEYFile uploadsAdmin Console
AWS_REGIONFile uploadsAdmin Console
CRON_SECRETCron job authAdmin Console
NEXT_PUBLIC_READER_URLCampaign preview linksAdmin Console
NEXT_PUBLIC_ADMIN_API_URLCampaign data fetchReader Portal
NEXT_PUBLIC_GA_MEASUREMENT_IDGA4 analyticsReader Portal
SENDGRID_WEBHOOK_VERIFICATION_KEYWebhook signature verificationAdmin Console
MAILCHIMP_WEBHOOK_KEYWebhook signature verificationAdmin Console

On this page

1. Executive OverviewWhat It DoesWho Uses ItBusiness Problem It Solves2. Feature ScopeIn ScopeOut of ScopeRelated Modules / Dependencies3. Roles & PermissionsMarketing PermissionsAdditional Related PermissionsRBAC Configuration4. Architecture & Design OverviewSystem ArchitectureKey Entities & ModelsWorkflow / State Models5. Prerequisites & Setup RequirementsCritical Prerequisites (Must be configured before marketing features work)1. Email Provider Configuration2. Email Templates3. Email Preferences & Branding4. Strapi CMS Configuration (for Newsletters only)5. S3 Storage Configuration (for file uploads)6. Cron Job Configuration7. Webhook Configuration (for email engagement tracking)8. Reader Portal URL Configuration9. RBAC Permission Assignment10. GA4 Configuration (for conversion surface analytics)6. Sub-Module: Audience ContactsPurposeAccessKey Features6.1 View Contacts6.2 Add Single Contact6.3 Bulk Import via CSV6.4 Edit Contact6.5 Delete Contact6.6 Export Contacts6.7 Auto-Created ContactsBusiness Rules7. Sub-Module: User SegmentsPurposeAccessKey Features7.1 View Segments7.2 Create Segment7.3 Edit Segment7.4 Preview Members7.5 Export Segment7.6 Delete SegmentBusiness Rules8. Sub-Module: Outreach CampaignsPurposeAccessCampaign Types8.1 Create Campaign (7-Step Wizard)8.2 Edit Campaign8.3 Campaign Detail Page (7 Tabs)8.4 Layout & Style Tab8.5 QR Code Tab8.6 Statistics Tab9. Sub-Module: Campaign Publishing & Landing PagesPurposePublishing WorkflowPublish a CampaignUnpublish a CampaignComplete a CampaignArchive a CampaignLanding Page URLLanding Page Behavior by StatusEvent-Type Campaign: 3-State Rendering10. Sub-Module: Campaign Submissions & ReviewPurposeAccess10.1 View Submissions10.2 Review a Submission10.3 Generate Promo Code10.4 Bulk Operations10.5 Submission Data Flow11. Sub-Module: Campaign InvitationsPurposeAccess11.1 View Invitations11.2 Create Invitation11.3 Send Invitation11.4 Scheduled Invitations11.5 Template Variable Resolution12. Sub-Module: Email CampaignsPurposeAccess12.1 View Email Campaigns12.2 Create Email Campaign12.3 Send Email Campaign12.4 Duplicate Campaign12.5 Email Campaign Metrics12.6 Delete Email Campaign13. Sub-Module: Newsletter EditionsPurposeArchitecture NoteAccess13.1 View Newsletter Editions13.2 Create Newsletter Edition13.3 Publish Newsletter13.4 Send Newsletter as Email Campaign13.5 Reader Portal Newsletter Archive14. Sub-Module: Promotional CodesPurposeAccess14.1 Two Types of Promo Codes14.2 Create Admin Coupon14.3 Campaign Promo Code Auto-Generation14.4 Public Promo Code Validation & Redemption15. Sub-Module: Conversion SurfacesPurposeAccess15.1 Surface Types15.2 Create Conversion Surface15.3 Activate Surface15.4 Reader Portal Rendering15.5 Analytics15.6 Coupon Integration16. Sub-Module: Campaign TypesPurposeAccessFeatures17. Cron Jobs & AutomationOverviewsend-scheduled-emailscampaign-statussend-invitations18. Webhook IntegrationsSendGrid Engagement WebhookMailchimp/Mandrill Engagement Webhook19. Reader Portal RenderingCampaign Landing PagesNewsletter ArchivePrograms Discovery PageConversion Surfaces20. Business Logic & RulesCampaign Validation RulesSegment Resolution RulesEmail Campaign RulesConversion Surface RulesPromotional Code RulesContact/Audience Rules21. End-to-End User FlowsFlow 1: Create and Launch an Outreach CampaignFlow 2: Newsletter Edition → Email CampaignFlow 3: Deploy a Conversion Surface