* Add newsletter worker: weekly community components email via Resend New Cloudflare Worker (cloudflare-workers/newsletter/) that composes and sends a weekly email featuring trending components (one Skill, Agent, MCP, Hook and Setting per send). Selection is weighted-random by recent downloads and all copy rotates from pools so no two emails read the same. Delivered as a Resend Broadcast to the segment in RESEND_SEGMENT_ID, with per-recipient unsubscribe handled by Resend, reply-to routing, and open/click tracking via track.aitmpl.com. Cron: Sundays 16:00 UTC (slot freed by decommissioning the claude-docs-monitor worker, documented in CLAUDE.md). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Address review: escape URL in HTML, surface broadcast id, cron dedupe guard - escapeHtml() on the component URL in the HTML href/text, consistent with every other dynamic field in the block - Include the created broadcast id in the error when POST /broadcasts/{id}/send fails, so an orphaned broadcast can be recovered manually - Guard the scheduled path against Cloudflare's occasional cron double-fire: pre-flight check for an existing non-draft "newsletter <today>" broadcast before sending (fails open; manual /trigger bypasses it so pilots can send multiple times a day) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Address review: log non-2xx Sentry responses in newsletter sentry.js reportError and checkIn now check response.ok and console.error rejected envelopes (invalid DSN, auth failure, rate limiting) so observability failures leave a signal in worker logs instead of passing silently. The never-throws guarantee is unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Address review: close cron double-fire race with draft leader election The read-then-create pre-check left a window where two overlapping cron invocations could both pass the check and both send. The scheduled path now runs a stateless leader election on Resend itself: each run creates its broadcast as a draft, waits a 10s settle so concurrent drafts are visible, lists today's broadcasts, and only the deterministic winner (smallest draft id, and only if nobody already sent) calls /send; losers delete their draft and yield. Chosen over Workers KV because KV has no compare-and-set and cannot provide an atomic lock; a Durable Object would, but is overkill for this worker. Manual /trigger sends still bypass dedupe. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Address review: prevent stale drafts from blocking same-day cron retries Two guards against a transient /send failure leaving an abandoned draft that would win every later election with nobody to send it: - On /send failure the run now deletes its own draft before throwing, so a retry starts clean (if the send actually succeeded despite the error response, the broadcast is no longer a draft and the DELETE is a no-op). - The election only yields to drafts created within the 5-minute overlap window; older drafts belong to runs that died before cleanup and are ignored as candidates. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Newsletter — Weekly Community Components Email
Cloudflare Worker that composes and sends a simple weekly email featuring trending components (one Skill, Agent, MCP, Hook and Setting per send, in that fixed order) as a Resend Broadcast.
- Rotating selection: each category pick is weighted-random by recent downloads — popular components appear more often, but every send differs.
- Rotating copy: subjects, catalog intro, per-component sentences, stat phrasing and closers are drawn from pools, so no two emails read the same.
- Minimal formatting: plain-text body plus a simple HTML version (bold + underlined component titles, clickable links, no styling beyond that).
- Unsubscribe built-in: the body carries
{{{RESEND_UNSUBSCRIBE_URL}}}; Resend replaces it per recipient, hosts the unsubscribe page, and skips unsubscribed contacts on future broadcasts automatically. - Replies go to
NEWSLETTER_REPLY_TO. - Tracking: open/click tracking enabled on the
aitmpl.comdomain with tracking subdomaintrack.aitmpl.com. Per-broadcast metrics (delivered, opens, clicks per link) at resend.com/broadcasts. - Data sources:
https://www.aitmpl.com/trending-data.jsonand/components.json(static dashboard assets).
Safety gate
The broadcast targets the segment in the RESEND_SEGMENT_ID secret. Point it
at a small pilot segment for tests, or the full-audience segment for
community-wide sends. Nothing outside that segment can receive the email.
Schedule
Cron: Sundays 16:00 UTC (0 16 * * SUN). Note: the Cloudflare account's free
plan allows 5 cron triggers; this slot was freed by decommissioning the
claude-docs-monitor worker (2026-07).
Endpoints
# Preview the generated email WITHOUT sending (hit repeatedly to see rotation)
curl "https://aitmpl-newsletter.<subdomain>.workers.dev/preview?format=text" \
-H "Authorization: Bearer $TRIGGER_SECRET"
# Dry run (full runner, no send)
curl -X POST "https://aitmpl-newsletter.<subdomain>.workers.dev/trigger?send=false" \
-H "Authorization: Bearer $TRIGGER_SECRET"
# Real send: creates + sends a Broadcast to RESEND_SEGMENT_ID
curl -X POST "https://aitmpl-newsletter.<subdomain>.workers.dev/trigger" \
-H "Authorization: Bearer $TRIGGER_SECRET"
# Health
curl "https://aitmpl-newsletter.<subdomain>.workers.dev/status"
Development & Deploy
cd cloudflare-workers/newsletter
npm run dev # Local dev (http://localhost:8787)
npx wrangler deploy # Deploy
Configuration
Public vars live in wrangler.toml [vars]: DASHBOARD_URL,
RESEND_FROM_EMAIL.
Secrets (via wrangler secret put <KEY>):
| Secret | Purpose |
|---|---|
RESEND_API_KEY |
Resend full access API key (broadcasts + segments) |
RESEND_SEGMENT_ID |
Segment the broadcast targets (pilot or full audience) |
NEWSLETTER_REPLY_TO |
Reply-to address for the newsletter |
TRIGGER_SECRET |
Auth for /trigger and /preview |
SENTRY_DSN |
Optional — error reporting + cron check-ins (newsletter-weekly monitor slug) |
Growing the audience
Contacts are loaded from Clerk (production instance) into Resend segments.
The pilot batches were loaded with an ad-hoc script (Clerk API → resend contacts create + add-segment); the full-audience sync follows the same
pattern over all users. Resend excludes unsubscribed/bounced contacts
automatically.