newsletter-publishing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Newsletter publishing

新闻通讯发布

Practical workflows for building and managing email newsletters for journalism and academia.
面向新闻与学术领域的电子邮件通讯构建与管理实用工作流。

When to activate

适用场景

  • Creating a new newsletter from scratch
  • Designing email templates for journalism content
  • Building and segmenting subscriber lists
  • Analyzing newsletter performance metrics
  • Planning editorial calendars for newsletters
  • Migrating between newsletter platforms
  • Improving deliverability and open rates
  • 从零开始创建新的新闻通讯
  • 为新闻内容设计邮件模板
  • 构建并细分订阅者列表
  • 分析新闻通讯绩效指标
  • 规划新闻通讯编辑日历
  • 在不同通讯平台间迁移
  • 提升送达率与打开率

Newsletter architecture

新闻通讯架构

Content strategy framework

内容策略框架

markdown
undefined
markdown
undefined

Newsletter strategy document

新闻通讯策略文档

Core identity

核心定位

  • Name:
  • Tagline (one line):
  • What readers get: [specific value proposition]
  • Frequency: [ ] Daily [ ] Weekly [ ] Bi-weekly [ ] Monthly
  • 名称:
  • 标语(一句话):
  • 读者收益: [具体价值主张]
  • 发送频率: [ ] 每日 [ ] 每周 [ ] 每两周 [ ] 每月

Target audience

目标受众

  • Primary reader:
  • What they care about:
  • Why they'll subscribe:
  • What they'll do with this info:
  • 核心读者:
  • 他们关注的内容:
  • 订阅动机:
  • 他们会如何使用这些信息:

Content pillars

内容支柱

  1. [Core topic 1] - [how often]
  2. [Core topic 2] - [how often]
  3. [Recurring feature] - [how often]
  1. [核心主题1] - [更新频率]
  2. [核心主题2] - [更新频率]
  3. [固定栏目] - [更新频率]

Voice and tone

语气风格

  • Formal ↔ Conversational: [1-5]
  • Serious ↔ Light: [1-5]
  • Reported ↔ Personal: [1-5]
  • 正式 ↔ 口语化: [1-5]
  • 严肃 ↔ 轻松: [1-5]
  • 客观报道 ↔ 个人观点: [1-5]

Success metrics (first 6 months)

初期成功指标(前6个月)

  • Subscriber goal:
  • Target open rate:
  • Target click rate:
undefined
  • 订阅者目标:
  • 目标打开率:
  • 目标点击率:
undefined

Issue structure template

单期通讯结构模板

markdown
undefined
markdown
undefined

[Newsletter Name] - Issue #[XX]

[新闻通讯名称] - 第#[XX]期

Date: [Date] Subject line: [Subject] Preview text: [First 50-90 characters readers see]

日期: [日期] 主题: [主题] 预览文本: [读者会看到的前50-90个字符]

Opening hook

开篇钩子

[2-3 sentences that make readers want to keep reading]
[2-3句话,吸引读者继续阅读]

Main story

主要内容

[Your primary content - 300-600 words for most newsletters]
[核心内容 - 大多数通讯建议300-600字]

Secondary items (if applicable)

次要内容(可选)

  • Quick hit 1: [Brief item with link]
  • Quick hit 2: [Brief item with link]
  • 速览1: [带链接的简短内容]
  • 速览2: [带链接的简短内容]

Recurring section

固定栏目

[Weekly column, data point, recommendation, etc.]
[每周专栏、数据点、推荐内容等]

Sign-off

结尾

[Personal note, call to action, or preview of next issue]

Unsubscribe | Preferences | Forward to a friend
undefined
[个人留言、行动号召或下期预告]

取消订阅 | 偏好设置 | 转发给好友
undefined

Technical implementation

技术实现

HTML email template (responsive)

响应式HTML邮件模板

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{newsletter_name}}</title>
  <style>
    /* Reset styles for email clients */
    body { margin: 0; padding: 0; width: 100%; }
    table { border-collapse: collapse; }
    img { border: 0; display: block; }

    /* Responsive container */
    .container {
      max-width: 600px;
      margin: 0 auto;
      font-family: Georgia, serif;
      font-size: 18px;
      line-height: 1.6;
      color: #333;
    }

    /* Dark mode support */
    @media (prefers-color-scheme: dark) {
      .container { background-color: #1a1a1a; color: #e0e0e0; }
      a { color: #6db3f2; }
    }

    /* Mobile styles */
    @media only screen and (max-width: 480px) {
      .container { padding: 15px !important; }
      h1 { font-size: 24px !important; }
    }
  </style>
</head>
<body>
  <table role="presentation" width="100%">
    <tr>
      <td align="center" style="padding: 20px;">
        <div class="container">
          <!-- Header -->
          <table width="100%">
            <tr>
              <td style="padding-bottom: 20px; border-bottom: 2px solid #333;">
                <h1 style="margin: 0;">{{newsletter_name}}</h1>
                <p style="margin: 5px 0 0; color: #666;">{{issue_date}}</p>
              </td>
            </tr>
          </table>

          <!-- Content -->
          <table width="100%">
            <tr>
              <td style="padding: 30px 0;">
                {{content}}
              </td>
            </tr>
          </table>

          <!-- Footer -->
          <table width="100%">
            <tr>
              <td style="padding-top: 20px; border-top: 1px solid #ddd; font-size: 14px; color: #666;">
                <p>You're receiving this because you subscribed to {{newsletter_name}}.</p>
                <p>
                  <a href="{{unsubscribe_url}}">Unsubscribe</a> |
                  <a href="{{preferences_url}}">Update preferences</a>
                </p>
              </td>
            </tr>
          </table>
        </div>
      </td>
    </tr>
  </table>
</body>
</html>
html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{newsletter_name}}</title>
  <style>
    /* Reset styles for email clients */
    body { margin: 0; padding: 0; width: 100%; }
    table { border-collapse: collapse; }
    img { border: 0; display: block; }

    /* Responsive container */
    .container {
      max-width: 600px;
      margin: 0 auto;
      font-family: Georgia, serif;
      font-size: 18px;
      line-height: 1.6;
      color: #333;
    }

    /* Dark mode support */
    @media (prefers-color-scheme: dark) {
      .container { background-color: #1a1a1a; color: #e0e0e0; }
      a { color: #6db3f2; }
    }

    /* Mobile styles */
    @media only screen and (max-width: 480px) {
      .container { padding: 15px !important; }
      h1 { font-size: 24px !important; }
    }
  </style>
</head>
<body>
  <table role="presentation" width="100%">
    <tr>
      <td align="center" style="padding: 20px;">
        <div class="container">
          <!-- Header -->
          <table width="100%">
            <tr>
              <td style="padding-bottom: 20px; border-bottom: 2px solid #333;">
                <h1 style="margin: 0;">{{newsletter_name}}</h1>
                <p style="margin: 5px 0 0; color: #666;">{{issue_date}}</p>
              </td>
            </tr>
          </table>

          <!-- Content -->
          <table width="100%">
            <tr>
              <td style="padding: 30px 0;">
                {{content}}
              </td>
            </tr>
          </table>

          <!-- Footer -->
          <table width="100%">
            <tr>
              <td style="padding-top: 20px; border-top: 1px solid #ddd; font-size: 14px; color: #666;">
                <p>You're receiving this because you subscribed to {{newsletter_name}}.</p>
                <p>
                  <a href="{{unsubscribe_url}}">Unsubscribe</a> |
                  <a href="{{preferences_url}}">Update preferences</a>
                </p>
              </td>
            </tr>
          </table>
        </div>
      </td>
    </tr>
  </table>
</body>
</html>

Python newsletter sender

Python新闻通讯发送器

python
from dataclasses import dataclass, field
from datetime import datetime
from typing import List, Dict, Optional
from enum import Enum
import hashlib

class SubscriberStatus(Enum):
    ACTIVE = "active"
    UNSUBSCRIBED = "unsubscribed"
    BOUNCED = "bounced"
    COMPLAINED = "complained"

@dataclass
class Subscriber:
    email: str
    name: Optional[str] = None
    subscribed_at: datetime = field(default_factory=datetime.now)
    status: SubscriberStatus = SubscriberStatus.ACTIVE
    tags: List[str] = field(default_factory=list)
    custom_fields: Dict = field(default_factory=dict)

    @property
    def hash_id(self) -> str:
        """Generate unique ID for unsubscribe links."""
        return hashlib.md5(self.email.encode()).hexdigest()[:12]

@dataclass
class NewsletterIssue:
    subject: str
    preview_text: str
    html_content: str
    plain_text: str
    scheduled_at: Optional[datetime] = None
    sent_at: Optional[datetime] = None
    issue_number: int = 0

    # Metrics
    sent_count: int = 0
    delivered_count: int = 0
    opened_count: int = 0
    clicked_count: int = 0
    bounced_count: int = 0
    unsubscribed_count: int = 0

    @property
    def open_rate(self) -> float:
        if self.delivered_count == 0:
            return 0.0
        return (self.opened_count / self.delivered_count) * 100

    @property
    def click_rate(self) -> float:
        if self.delivered_count == 0:
            return 0.0
        return (self.clicked_count / self.delivered_count) * 100

class NewsletterManager:
    """Core newsletter operations."""

    def __init__(self, name: str):
        self.name = name
        self.subscribers: List[Subscriber] = []
        self.issues: List[NewsletterIssue] = []

    def add_subscriber(self, email: str, name: str = None,
                       tags: List[str] = None) -> Subscriber:
        """Add new subscriber with double opt-in pending."""
        sub = Subscriber(
            email=email.lower().strip(),
            name=name,
            tags=tags or []
        )
        self.subscribers.append(sub)
        return sub

    def segment_subscribers(self, tags: List[str] = None,
                           min_engagement: float = None) -> List[Subscriber]:
        """Get subscribers matching criteria."""
        active = [s for s in self.subscribers
                  if s.status == SubscriberStatus.ACTIVE]

        if tags:
            active = [s for s in active
                     if any(t in s.tags for t in tags)]

        return active

    def calculate_engagement_score(self, subscriber: Subscriber) -> float:
        """Score subscriber engagement 0-100."""
        # Implementation would track opens/clicks per subscriber
        return 50.0  # Placeholder
python
from dataclasses import dataclass, field
from datetime import datetime
from typing import List, Dict, Optional
from enum import Enum
import hashlib

class SubscriberStatus(Enum):
    ACTIVE = "active"
    UNSUBSCRIBED = "unsubscribed"
    BOUNCED = "bounced"
    COMPLAINED = "complained"

@dataclass
class Subscriber:
    email: str
    name: Optional[str] = None
    subscribed_at: datetime = field(default_factory=datetime.now)
    status: SubscriberStatus = SubscriberStatus.ACTIVE
    tags: List[str] = field(default_factory=list)
    custom_fields: Dict = field(default_factory=dict)

    @property
    def hash_id(self) -> str:
        """Generate unique ID for unsubscribe links."""
        return hashlib.md5(self.email.encode()).hexdigest()[:12]

@dataclass
class NewsletterIssue:
    subject: str
    preview_text: str
    html_content: str
    plain_text: str
    scheduled_at: Optional[datetime] = None
    sent_at: Optional[datetime] = None
    issue_number: int = 0

    # Metrics
    sent_count: int = 0
    delivered_count: int = 0
    opened_count: int = 0
    clicked_count: int = 0
    bounced_count: int = 0
    unsubscribed_count: int = 0

    @property
    def open_rate(self) -> float:
        if self.delivered_count == 0:
            return 0.0
        return (self.opened_count / self.delivered_count) * 100

    @property
    def click_rate(self) -> float:
        if self.delivered_count == 0:
            return 0.0
        return (self.clicked_count / self.delivered_count) * 100

class NewsletterManager:
    """Core newsletter operations."""

    def __init__(self, name: str):
        self.name = name
        self.subscribers: List[Subscriber] = []
        self.issues: List[NewsletterIssue] = []

    def add_subscriber(self, email: str, name: str = None,
                       tags: List[str] = None) -> Subscriber:
        """Add new subscriber with double opt-in pending."""
        sub = Subscriber(
            email=email.lower().strip(),
            name=name,
            tags=tags or []
        )
        self.subscribers.append(sub)
        return sub

    def segment_subscribers(self, tags: List[str] = None,
                           min_engagement: float = None) -> List[Subscriber]:
        """Get subscribers matching criteria."""
        active = [s for s in self.subscribers
                  if s.status == SubscriberStatus.ACTIVE]

        if tags:
            active = [s for s in active
                     if any(t in s.tags for t in tags)]

        return active

    def calculate_engagement_score(self, subscriber: Subscriber) -> float:
        """Score subscriber engagement 0-100."""
        # Implementation would track opens/clicks per subscriber
        return 50.0  # Placeholder

Subscriber management

订阅者管理

List hygiene workflow

列表清理工作流

python
from datetime import datetime, timedelta

def clean_subscriber_list(manager: NewsletterManager,
                         inactive_threshold_days: int = 180) -> dict:
    """Identify and handle inactive subscribers."""
    cutoff = datetime.now() - timedelta(days=inactive_threshold_days)

    results = {
        'total': len(manager.subscribers),
        'active': 0,
        'inactive': [],
        'bounced': [],
        'unsubscribed': []
    }

    for sub in manager.subscribers:
        if sub.status == SubscriberStatus.BOUNCED:
            results['bounced'].append(sub.email)
        elif sub.status == SubscriberStatus.UNSUBSCRIBED:
            results['unsubscribed'].append(sub.email)
        elif sub.status == SubscriberStatus.ACTIVE:
            # Check last engagement
            engagement = manager.calculate_engagement_score(sub)
            if engagement < 10:  # Very low engagement
                results['inactive'].append(sub.email)
            else:
                results['active'] += 1

    return results

def run_reengagement_campaign(inactive_subscribers: List[str]) -> None:
    """Send win-back campaign to inactive subscribers."""
    # Send "We miss you" campaign
    # If no engagement after 2 attempts, mark for removal
    pass
python
from datetime import datetime, timedelta

def clean_subscriber_list(manager: NewsletterManager,
                         inactive_threshold_days: int = 180) -> dict:
    """Identify and handle inactive subscribers."""
    cutoff = datetime.now() - timedelta(days=inactive_threshold_days)

    results = {
        'total': len(manager.subscribers),
        'active': 0,
        'inactive': [],
        'bounced': [],
        'unsubscribed': []
    }

    for sub in manager.subscribers:
        if sub.status == SubscriberStatus.BOUNCED:
            results['bounced'].append(sub.email)
        elif sub.status == SubscriberStatus.UNSUBSCRIBED:
            results['unsubscribed'].append(sub.email)
        elif sub.status == SubscriberStatus.ACTIVE:
            # Check last engagement
            engagement = manager.calculate_engagement_score(sub)
            if engagement < 10:  # Very low engagement
                results['inactive'].append(sub.email)
            else:
                results['active'] += 1

    return results

def run_reengagement_campaign(inactive_subscribers: List[str]) -> None:
    """Send win-back campaign to inactive subscribers."""
    # Send "We miss you" campaign
    # If no engagement after 2 attempts, mark for removal
    pass

Subscriber segmentation

订阅者细分

markdown
undefined
markdown
undefined

Recommended segments

推荐细分维度

By engagement

按参与度

  • VIPs: Open rate > 80%, always click
  • Engaged: Open rate 40-80%
  • Casual: Open rate 10-40%
  • At-risk: Haven't opened in 90 days
  • Inactive: Haven't opened in 180 days
  • 核心用户: 打开率>80%,每次都点击
  • 活跃用户: 打开率40-80%
  • 普通用户: 打开率10-40%
  • 风险用户: 90天未打开
  • 沉睡用户: 180天未打开

By interest (tag-based)

按兴趣(基于标签)

  • Topic preferences from signup
  • Content they've clicked
  • Surveys/polls they've answered
  • 注册时选择的主题偏好
  • 点击过的内容
  • 参与过的调研/投票

By source

按来源

  • Organic (website signup)
  • Referral (forwarded by friend)
  • Social media
  • Paywall/registration wall
undefined
  • 自然流量(官网注册)
  • 推荐(好友转发)
  • 社交媒体
  • 付费墙/注册墙
undefined

Subject line optimization

主题行优化

High-performing patterns

高绩效主题行模式

markdown
undefined
markdown
undefined

Subject line formulas that work

有效的主题行公式

For news/journalism

新闻/资讯类

  • Breaking format: "Breaking: [Concise news]"
  • Numbers: "[X] things we learned about [topic]"
  • Question: "Why did [entity] do [thing]?"
  • Direct: "[Topic]: What you need to know"
  • 突发格式: "突发:[简洁新闻内容]"
  • 数字型: "关于[主题]的[X]个关键发现"
  • 疑问型: "为什么[主体]会做出[行为]?"
  • 直接型: "[主题]:你需要了解的内容"

For analysis/opinion

分析/观点类

  • Take: "The real story behind [event]"
  • Contrarian: "Why everyone is wrong about [topic]"
  • Insider: "What [industry] insiders know about [topic]"
  • 深度解读: "[事件]背后的真相"
  • 逆向观点: "为什么所有人都误解了[主题]"
  • 内部视角: "[行业]从业者才知道的[主题]内幕"

What to avoid

需避免的雷区

  • ALL CAPS
  • Excessive punctuation!!!
  • Clickbait that doesn't deliver
  • Spam trigger words (FREE, URGENT, ACT NOW)
  • Misleading preview text
undefined
  • 全大写字母
  • 过多标点符号!!!
  • 无法兑现的标题党
  • 垃圾邮件触发词(FREE、URGENT、ACT NOW)
  • 误导性预览文本
undefined

A/B testing framework

A/B测试框架

python
import random
from typing import List, Tuple

def ab_test_subject_lines(subscribers: List[Subscriber],
                         subject_a: str,
                         subject_b: str,
                         test_percentage: float = 0.2) -> dict:
    """
    Test two subject lines on subset before full send.
    """
    test_size = int(len(subscribers) * test_percentage)
    test_group = random.sample(subscribers, test_size)

    # Split test group
    half = len(test_group) // 2
    group_a = test_group[:half]
    group_b = test_group[half:]

    remaining = [s for s in subscribers if s not in test_group]

    return {
        'group_a': {
            'subject': subject_a,
            'subscribers': group_a,
            'size': len(group_a)
        },
        'group_b': {
            'subject': subject_b,
            'subscribers': group_b,
            'size': len(group_b)
        },
        'remaining': {
            'subscribers': remaining,
            'size': len(remaining),
            'note': 'Send winner to this group after test period'
        },
        'test_duration_hours': 4
    }
python
import random
from typing import List, Tuple

def ab_test_subject_lines(subscribers: List[Subscriber],
                         subject_a: str,
                         subject_b: str,
                         test_percentage: float = 0.2) -> dict:
    """
    Test two subject lines on subset before full send.
    """
    test_size = int(len(subscribers) * test_percentage)
    test_group = random.sample(subscribers, test_size)

    # Split test group
    half = len(test_group) // 2
    group_a = test_group[:half]
    group_b = test_group[half:]

    remaining = [s for s in subscribers if s not in test_group]

    return {
        'group_a': {
            'subject': subject_a,
            'subscribers': group_a,
            'size': len(group_a)
        },
        'group_b': {
            'subject': subject_b,
            'subscribers': group_b,
            'size': len(group_b)
        },
        'remaining': {
            'subscribers': remaining,
            'size': len(remaining),
            'note': 'Send winner to this group after test period'
        },
        'test_duration_hours': 4
    }

Deliverability best practices

送达率最佳实践

Email authentication setup

邮件认证设置

markdown
undefined
markdown
undefined

DNS records for deliverability

提升送达率的DNS记录

SPF record

SPF记录

v=spf1 include:_spf.youresp.com ~all
v=spf1 include:_spf.youresp.com ~all

DKIM

DKIM

  • Generate keys through your ESP
  • Add TXT record with public key
  • Verify signature is applied to outgoing mail
  • 通过邮件服务提供商(ESP)生成密钥
  • 添加包含公钥的TXT记录
  • 验证签名已应用于 outgoing 邮件

DMARC

DMARC

v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com
v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com

Checklist before sending

发送前检查清单

  • SPF, DKIM, DMARC configured
  • Sending domain warmed up
  • List is clean (no hard bounces)
  • Unsubscribe link works
  • Physical address in footer (CAN-SPAM)
  • Test email received in inbox (not spam)
undefined
  • 已配置SPF、DKIM、DMARC
  • 发送域名已完成预热
  • 列表已清理(无硬退信)
  • 取消订阅链接可用
  • 页脚包含物理地址(符合CAN-SPAM)
  • 测试邮件已进入收件箱(而非垃圾邮件)
undefined

Spam score checklist

垃圾邮件评分检查清单

markdown
undefined
markdown
undefined

Before you send

发送前检查

Content checks

内容检查

  • No spam trigger words
  • Text-to-image ratio good (mostly text)
  • All links are to reputable domains
  • No URL shorteners (use full links)
  • Plain text version included
  • 无垃圾邮件触发词
  • 文本与图片比例合理(以文本为主)
  • 所有链接指向可信域名
  • 不使用短链接(使用完整链接)
  • 包含纯文本版本

Technical checks

技术检查

  • From address matches sending domain
  • Reply-to address is monitored
  • Preheader text is set
  • Images have alt text
  • Links are not broken
undefined
  • 发件人地址与发送域名匹配
  • 回复地址已被监控
  • 已设置预览文本
  • 图片包含替代文本
  • 链接无失效
undefined

Analytics and optimization

分析与优化

Key metrics dashboard

核心指标仪表盘

python
from dataclasses import dataclass

@dataclass
class NewsletterAnalytics:
    """Track newsletter performance over time."""

    issue: NewsletterIssue

    def summary(self) -> dict:
        return {
            'issue_number': self.issue.issue_number,
            'sent': self.issue.sent_count,
            'delivered': self.issue.delivered_count,
            'delivery_rate': self._pct(self.issue.delivered_count,
                                       self.issue.sent_count),
            'opens': self.issue.opened_count,
            'open_rate': self.issue.open_rate,
            'clicks': self.issue.clicked_count,
            'click_rate': self.issue.click_rate,
            'click_to_open': self._pct(self.issue.clicked_count,
                                       self.issue.opened_count),
            'unsubscribes': self.issue.unsubscribed_count,
            'unsubscribe_rate': self._pct(self.issue.unsubscribed_count,
                                          self.issue.delivered_count),
        }

    def _pct(self, numerator: int, denominator: int) -> float:
        if denominator == 0:
            return 0.0
        return round((numerator / denominator) * 100, 2)
python
from dataclasses import dataclass

@dataclass
class NewsletterAnalytics:
    """Track newsletter performance over time."""

    issue: NewsletterIssue

    def summary(self) -> dict:
        return {
            'issue_number': self.issue.issue_number,
            'sent': self.issue.sent_count,
            'delivered': self.issue.delivered_count,
            'delivery_rate': self._pct(self.issue.delivered_count,
                                       self.issue.sent_count),
            'opens': self.issue.opened_count,
            'open_rate': self.issue.open_rate,
            'clicks': self.issue.clicked_count,
            'click_rate': self.issue.click_rate,
            'click_to_open': self._pct(self.issue.clicked_count,
                                       self.issue.opened_count),
            'unsubscribes': self.issue.unsubscribed_count,
            'unsubscribe_rate': self._pct(self.issue.unsubscribed_count,
                                          self.issue.delivered_count),
        }

    def _pct(self, numerator: int, denominator: int) -> float:
        if denominator == 0:
            return 0.0
        return round((numerator / denominator) * 100, 2)

Benchmarks (journalism newsletters)

Benchmarks (journalism newsletters)

BENCHMARKS = { 'open_rate': {'good': 40, 'excellent': 55}, 'click_rate': {'good': 4, 'excellent': 8}, 'unsubscribe_rate': {'acceptable': 0.5, 'concerning': 1.0}, }
undefined
BENCHMARKS = { 'open_rate': {'good': 40, 'excellent': 55}, 'click_rate': {'good': 4, 'excellent': 8}, 'unsubscribe_rate': {'acceptable': 0.5, 'concerning': 1.0}, }
undefined

Platform comparison

平台对比

PlatformBest forPricing modelKey feature
SubstackWriter-first, paid subsRevenue shareBuilt-in payments
ButtondownDevelopers, minimalPer subscriberMarkdown native
GhostPublishers, membershipsFlat feeFull CMS included
beehiivGrowth-focusedFreemiumReferral tools
ConvertKitCreatorsPer subscriberAutomation
MailchimpSmall orgsTieredEasy templates
平台适用场景定价模式核心功能
Substack以写作者为核心,支持付费订阅收入分成内置支付功能
Buttondown开发者群体,极简风格按订阅者数量付费原生支持Markdown
Ghost出版机构,会员体系固定费用集成完整CMS
beehiiv增长导向的运营者免费增值模式推荐裂变工具
ConvertKit内容创作者按订阅者数量付费自动化工作流
Mailchimp小型机构分层定价易用模板库

Legal compliance

合规要求

CAN-SPAM requirements (US)

美国CAN-SPAM合规要求

markdown
- [ ] Accurate "From" name and email
- [ ] Non-deceptive subject line
- [ ] Physical postal address included
- [ ] Working unsubscribe mechanism
- [ ] Unsubscribe honored within 10 days
- [ ] No purchased lists
markdown
- [ ] 发件人名称与邮箱地址准确
- [ ] 主题行无欺骗性
- [ ] 包含物理邮寄地址
- [ ] 提供可用的取消订阅机制
- [ ] 10天内处理取消订阅请求
- [ ] 不使用购买的邮件列表

GDPR requirements (EU subscribers)

欧盟GDPR合规要求(针对欧盟订阅者)

markdown
- [ ] Explicit consent obtained (not pre-checked)
- [ ] Clear privacy policy linked
- [ ] Easy unsubscribe process
- [ ] Data export available on request
- [ ] Data deletion on request
- [ ] Record of consent stored
markdown
- [ ] 获取明确的订阅许可(非预先勾选)
- [ ] 链接清晰的隐私政策
- [ ] 提供便捷的取消订阅流程
- [ ] 可应请求导出用户数据
- [ ] 可应请求删除用户数据
- [ ] 留存订阅许可记录

Related skills

相关技能

  • web-scraping - Automate content gathering for newsletters
  • data-journalism - Include data visualizations in emails
  • academic-writing - Write clear, structured content

  • 网页爬取 - 自动收集通讯内容
  • 数据新闻 - 在邮件中加入数据可视化内容
  • 学术写作 - 撰写清晰、结构化的内容

Skill metadata

技能元数据

FieldValue
Version1.0.0
Created2025-12-26
AuthorClaude Skills for Journalism
DomainPublishing, Marketing
ComplexityIntermediate
字段
版本1.0.0
创建时间2025-12-26
作者Claude Skills for Journalism
领域出版、营销
复杂度中级