Newsletter
هذا المحتوى غير متوفر بلغتك بعد.
Newsletter Operations
Section titled “Newsletter Operations”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.
Architecture
Section titled “Architecture”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 emailSubscriber Flow
Section titled “Subscriber Flow”- User submits form on nself.org
- ping_api inserts subscriber with
is_verified=falseand sends T1 confirmation email - User clicks the confirmation link (valid for 48 hours)
- ping_api sets
is_verified=trueand sends W1 welcome email - Days 3, 7, 14, 21: W2–W5 welcome series (sent manually or via cron, see Welcome Series below)
- Bi-weekly: N1 issue sent to all
is_verified=true, status=activesubscribers
Required Environment Variables
Section titled “Required Environment Variables”| Variable | Description |
|---|---|
ELASTIC_EMAIL_API_KEY | Elastic Email API key (in vault as ELASTIC_EMAIL_ADMIN_API_KEY) |
ELASTIC_EMAIL_FROM | Sender address, noreplynself |
NEWSLETTER_CONFIRM_SECRET | HMAC secret for confirmation tokens |
NEWSLETTER_UNSUBSCRIBE_SECRET | HMAC secret for unsubscribe tokens |
ELASTIC_EMAIL_LIST_ID_NEWSLETTER | Elastic Email list ID (created on first deploy) |
Generate secrets with:
openssl rand -hex 32Email Templates
Section titled “Email Templates”Templates are MJML sources at web/org/src/email-templates/. Compile to HTML with:
cd web/orgpnpm email:preview # renders to localhost for previewpnpm email:compile # compiles all MJML to HTML| Template | When |
|---|---|
T1-confirm.mjml | Double opt-in confirmation |
T2-unsub.mjml | Unsubscribe confirmation |
T3-license-alert.mjml | License renewal -7 days |
W1-welcome.mjml | Immediately on confirm |
W2-first-plugin.mjml | Day 3 |
W3-case-study.mjml | Day 7 |
W4-nclaw.mjml | Day 14 |
W5-cloud-preview.mjml | Day 21 |
N1-biweekly.mjml | Every 2 weeks |
Sending a Bi-Weekly Issue
Section titled “Sending a Bi-Weekly Issue”-
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}}
-
Compile the template:
pnpm email:compile -
Upload to Elastic Email template library via API or dashboard
-
Send to list via Elastic Email campaigns UI or API
Subscriber Management
Section titled “Subscriber Management”Query active confirmed subscribers:
SELECT email, subscribed_atFROM newsletter_subscribersWHERE is_verified = true AND status = 'active'ORDER BY subscribed_at DESC;Check unconfirmed (pending double opt-in):
SELECT email, created_at, verification_expires_atFROM newsletter_subscribersWHERE is_verified = false AND verification_expires_at > NOW();Clean up expired unconfirmed:
DELETE FROM newsletter_subscribersWHERE is_verified = false AND verification_expires_at < NOW() AND created_at < NOW() - INTERVAL '7 days';Rate Limiting
Section titled “Rate Limiting”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_subscribersonly, 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