A Cloudflare Worker that summarizes forwarded newsletter emails, RSS feed articles, and web pages, then sends per-item summaries back to your Gmail inbox.
Gmail Filter -> Cloudflare Email Routing -> Worker -> Anthropic -> [OpenAI TTS] -> Resend -> Gmail
Cron (every 30 min) -> RSS Feeds -> Worker -> Anthropic -> [OpenAI TTS] -> Resend -> Gmail
Chrome Extension -> Readability.js -> Worker -> Anthropic -> [OpenAI TTS] -> Resend -> Gmail
iOS Shortcut -> Safari JS extraction -> Worker -> Anthropic -> [OpenAI TTS] -> Resend -> Gmail
Email path: Gmail keeps the original newsletter in your inbox. A Gmail filter forwards matching senders to a Cloudflare-managed email address, the Worker parses the message body, summarizes with claude-sonnet-4-6, and Resend sends the summary back to you.
RSS path: A Cloudflare Cron Trigger runs every 30 minutes, fetches configured RSS/Atom feeds, extracts new articles, summarizes them, and emails the summaries via Resend. Already-processed items are skipped using the same KV dedup store.
Chrome extension path: A Manifest V3 Chrome extension lets you save any article — including paywalled pages you're logged into — by clicking "Summarize This Page." The extension uses Mozilla's Readability.js to extract the article content in-browser, then POSTs it to the Worker's POST /api/save endpoint with Bearer token auth. The Worker deduplicates, summarizes, and emails the result.
iOS Shortcut path: An iOS Shortcut appears in Safari's share sheet, runs JavaScript on the current page to extract the article text (works with paywalled content since it runs in your authenticated session), and POSTs it to the same POST /api/save endpoint. No app or extension to install — just a Shortcut.
Article links: Summary emails from RSS feeds, the Chrome extension, and iOS Shortcuts include a "Read original" link back to the source article. Forwarded email summaries omit the link since newsletters don't have a single canonical URL. When Pushover is configured, tapping a push notification opens the original article directly.
npm install
npx wrangler loginCreate the KV namespaces and copy the returned IDs into wrangler.toml:
npx wrangler kv namespace create PROCESSED_EMAILS
npx wrangler kv namespace create PROCESSED_EMAILS --previewThen deploy:
npm run deploynpx wrangler secret put ANTHROPIC_API_KEY
npx wrangler secret put RESEND_API_KEY
npx wrangler secret put EMAIL_TO
npx wrangler secret put SUMMARY_FROM
npx wrangler secret put API_KEY-
API_KEY: shared Bearer token for the Chrome extension. Generate one withopenssl rand -hex 32. -
EMAIL_TO: the Gmail address that should receive summary emails. -
SUMMARY_FROM: a verified Resend sender on your domain. The Worker sends emails fromNewsletter Summary <SUMMARY_FROM>for forwarded emails orBlog Post Summary <SUMMARY_FROM>for RSS feed items. -
The Worker currently truncates extracted email text to 80,000 characters before summarization and caps model output at 1,024 tokens.
To receive an iOS push notification whenever a summary email is sent, set up Pushover:
- Create a Pushover account and install the Pushover iOS app ($5 one-time purchase).
- Create an application in the Pushover dashboard to get an API token.
- Set both secrets:
npx wrangler secret put PUSHOVER_USER_KEY
npx wrangler secret put PUSHOVER_API_TOKENWhen both are configured, each summary email will also trigger a push notification with the article title and summary. If either secret is missing, notifications are silently skipped.
To receive a linked MP3 audio version of each summary in the email, enable OpenAI TTS and set up a Cloudflare R2 bucket for audio storage:
# Create the R2 bucket
npx wrangler r2 bucket create tldr-tts-audioEnable public access on the bucket via the Cloudflare Dashboard (R2 > tldr-tts-audio > Settings > Public access) using either a custom domain or the r2.dev subdomain.
Optionally, add a lifecycle rule to auto-delete audio files after 90 days: R2 > tldr-tts-audio > Settings > Object lifecycle rules > Add rule > Delete objects after 90 days.
Then set the secrets:
npx wrangler secret put OPENAI_API_KEY
npx wrangler secret put TTS_ENABLED
# Enter: true
npx wrangler secret put TTS_AUDIO_PUBLIC_URL
# Enter: https://your-r2-public-domain.com (the public base URL of the bucket)Optionally set a playback speed (0.25–4.0, default 1.0):
npx wrangler secret put TTS_SPEED
# Enter: 1.2When configured, each summary email will include a "🎧 Listen to summary" link next to the "🔗 Read original" link at the top of the email. The default voice is alloy — you can change it by modifying the TTS_VOICE constant in src/worker.ts (options: alloy, echo, fable, onyx, nova, shimmer). Pricing is ~$0.00005 per summary ($15 per 1M characters). If TTS generation or upload fails, the email is still sent without the audio link.
Set the RSS_FEEDS environment variable to a JSON array of feed configs:
npx wrangler secret put RSS_FEEDS
# Enter: [{"url":"https://example.com/feed.xml","name":"Example Blog"}]Do not put your feed list in wrangler.toml — use wrangler secret put so it stays out of version control. The cron trigger runs every 30 minutes and processes up to 5 new items per feed per run.
You need a domain using Cloudflare as the authoritative nameserver.
- Enable Email Routing for the domain in Cloudflare.
- Create an address such as
newsletters@your-domain.comand route it to this Worker. - Make sure
EMAIL_TOis also a verified Cloudflare Email Routing destination address. The Worker forwards Gmail's forwarding-confirmation email there instead of trying to summarize it.
- In Gmail settings, add the Cloudflare address such as
newsletters@your-domain.comas a forwarding address. - Wait for Gmail's forwarding confirmation email to arrive in
EMAIL_TO, then approve the forwarding address in Gmail. - Create one or more Gmail filters for newsletter senders and choose
Forward it tothe Cloudflare address. - Keep the filters narrow enough that they do not match the summary emails coming back from
SUMMARY_FROM, or you will create a loop.
npm run devIn local development, Cloudflare exposes the email handler at /cdn-cgi/handler/email. You can post a raw .eml file to it or trigger the cron manually:
curl "http://localhost:8787/__scheduled?cron=*/30+*+*+*+*"For email testing:
curl -X POST http://127.0.0.1:8787/cdn-cgi/handler/email \
--url-query 'from=sender@example.com' \
--url-query 'to=newsletters@your-domain.com' \
-H 'Content-Type: message/rfc822' \
--data-binary @sample.emlRun the automated checks with:
npm run typecheck
npm test- Open
chrome://extensionsand enable Developer mode. - Click Load unpacked and select the
extension/directory. - Click the extension icon, enter your Worker URL (e.g.
https://tldr.your-domain.workers.dev) and theAPI_KEYyou set above, then click Save Settings. - Navigate to any article page and click Summarize This Page.
This adds a "Summarize This Page" option to Safari's share sheet on iOS. Because the JavaScript runs inside your Safari session, it can extract full article text from paywalled pages you're logged into.
-
Open the Shortcuts app on your iPhone or iPad.
-
Tap + to create a new shortcut. Tap the name at the top and choose Rename to give it a name like "Summarize This Page."
-
Tap the name again and choose Add to Share Sheet. Under Share Sheet Types, deselect everything except Safari web pages.
-
Add a Run JavaScript on Safari Web Page action and paste this script:
const article = { url: document.URL, title: document.title, content: document.body.innerText.substring(0, 80000), siteName: (document.querySelector('meta[property="og:site_name"]') || {}).content || window.location.hostname }; completion(article);
-
Add a Get Contents of URL action and configure it:
- URL:
https://tldr.your-domain.workers.dev/api/save(your Worker URL) - Method: POST
- Headers: add
Authorizationwith valueBearer <your API_KEY> - Request Body: JSON — add keys
url,title,content, andsiteName, setting each value to the corresponding field from the Run JavaScript on Safari Web Page output (use the variable picker).
- URL:
-
Optionally add a Show Notification action with the text "Summary sent!" so you get confirmation.
To use it: open any article in Safari, tap the Share button, and select Summarize This Page from the share sheet.
Forward one newsletter sender to the Cloudflare address and confirm:
- the original newsletter stays in Gmail
- one summary email arrives from Resend
- the summary email has no action buttons
Use npx wrangler tail to stream Worker logs while testing.