تخطَّ إلى المحتوى

Newsletter

هذا المحتوى غير متوفر بلغتك بعد.

The ɳSelf newsletter sends every two weeks. Infrastructure is built on Elastic Email via the ping_api service at ping.nself.org. Subscriber data lives in the newsletter_subscribers Postgres table, no third-party CRM.

Web form → POST /api/newsletter (Next.js proxy)
→ POST /newsletter/subscribe (ping_api)
→ Insert newsletter_subscribers (is_verified=false)
→ Send T1 confirmation email (Elastic Email)
User clicks confirm link → GET /newsletter/confirm?token=&email=
→ Verify HMAC token
→ Update is_verified=true
→ Send W1 welcome email
  1. User submits form on nself.org
  2. ping_api inserts subscriber with is_verified=false and sends T1 confirmation email
  3. User clicks the confirmation link (valid for 48 hours)
  4. ping_api sets is_verified=true and sends W1 welcome email
  5. Days 3, 7, 14, 21: W2–W5 welcome series (sent manually or via cron, see Welcome Series below)
  6. Bi-weekly: N1 issue sent to all is_verified=true, status=active subscribers
VariableDescription
ELASTIC_EMAIL_API_KEYElastic Email API key (in vault as ELASTIC_EMAIL_ADMIN_API_KEY)
ELASTIC_EMAIL_FROMSender address, noreplynself
NEWSLETTER_CONFIRM_SECRETHMAC secret for confirmation tokens
NEWSLETTER_UNSUBSCRIBE_SECRETHMAC secret for unsubscribe tokens
ELASTIC_EMAIL_LIST_ID_NEWSLETTERElastic Email list ID (created on first deploy)

Generate secrets with:

Terminal window
openssl rand -hex 32

Templates are MJML sources at web/org/src/email-templates/. Compile to HTML with:

Terminal window
cd web/org
pnpm email:preview # renders to localhost for preview
pnpm email:compile # compiles all MJML to HTML
TemplateWhen
T1-confirm.mjmlDouble opt-in confirmation
T2-unsub.mjmlUnsubscribe confirmation
T3-license-alert.mjmlLicense renewal -7 days
W1-welcome.mjmlImmediately on confirm
W2-first-plugin.mjmlDay 3
W3-case-study.mjmlDay 7
W4-nclaw.mjmlDay 14
W5-cloud-preview.mjmlDay 21
N1-biweekly.mjmlEvery 2 weeks
  1. Fill in the variables in N1-biweekly.mjml:

    • {{ISSUE_NUMBER}}, {{HEADLINE}}, {{RELEASE_HEADLINE}}, {{RELEASE_SUMMARY}}
    • {{CLI_SNIPPET}}, {{CHANGELOG_URL}}
    • {{COMMUNITY_SHOWCASE}}, {{DOCS_URL}}, {{DOCS_TITLE}}, {{DOCS_TEASER}}
    • {{ROADMAP_TEASER}}
  2. Compile the template: pnpm email:compile

  3. Upload to Elastic Email template library via API or dashboard

  4. Send to list via Elastic Email campaigns UI or API

Query active confirmed subscribers:

SELECT email, subscribed_at
FROM newsletter_subscribers
WHERE is_verified = true AND status = 'active'
ORDER BY subscribed_at DESC;

Check unconfirmed (pending double opt-in):

SELECT email, created_at, verification_expires_at
FROM newsletter_subscribers
WHERE is_verified = false
AND verification_expires_at > NOW();

Clean up expired unconfirmed:

DELETE FROM newsletter_subscribers
WHERE is_verified = false
AND verification_expires_at < NOW()
AND created_at < NOW() - INTERVAL '7 days';

The subscribe endpoint enforces 3 attempts per IP per hour via in-memory rate limiting in ping_api. This is sufficient for protecting against form spam without a Redis dependency.

  • Subscriber data is stored in newsletter_subscribers only, no third-party CRM sync
  • Unsubscribe link is included in every email footer
  • Unsubscribe takes effect within 10 seconds (direct DB update)
  • Double opt-in is required, unconfirmed subscribers never receive marketing emails