Job-alert emails pile up quickly and are hard to skim. Each message bundles many listings, and there is no good way to filter or search across them. This is a blueprint for a small pipeline that ingests those emails, extracts the individual listings with an LLM, and serves them as a clean RSS feed and a searchable web dashboard — entirely on Cloudflare’s free tier. It is meant as an idea to replicate, not a ready-made repository.
What
Four independent Workers backed by a single Cloudflare D1 (SQLite) database:
- Email Worker — receives incoming mail via Cloudflare Email Routing, parses it with
postal-mime, extracts job listings using Workers AI (Llama 3.3 70B), and stores them in D1. - Enrichment Worker — hourly cron that looks up unknown companies via the Brave Search API and caches a short description.
- RSS Worker — serves a paginated RSS feed on a custom domain.
- Dashboard Worker — a Tailwind + vanilla-JS SPA for browsing, searching, and deleting jobs and emails. Protected by Cloudflare Zero Trust (identity provider of choice).
Why
Converting the email stream into RSS makes the listings consumable in any feed reader, deduplicated by URL, and independent of the original inbox. Running on Workers keeps the whole stack on a single free-tier platform with no servers to maintain, and Workers AI removes the need for a separate LLM API key.
How
- Configure a dedicated address in Cloudflare Email Routing to trigger the Email Worker on every inbound message.
- In the Email Worker, parse the MIME body, send the text to Workers AI with a schema-constrained prompt, and store the extracted listings as a JSON blob in an
emailstable. Deduplicate at ingestion time onjob_url. - Schedule the Enrichment Worker as an hourly cron. It finds companies without a description in a
companiestable, queries Brave Search, and writes the result back. - In the RSS Worker, read from D1 and generate a paginated feed.
- In the Dashboard Worker, expose a small JSON API (
/api/jobs,/api/emails, stats, delete) consumed by the SPA. Gate access with a Cloudflare Zero Trust Access policy — an AAAA record pointingdashboardto100::plusworkers_dev = falsein the Wrangler config is enough to block*.workers.devbypass paths.
Snippets
The whole design fits in a handful of small pieces.
Schema — two tables in D1:
| |
Email Worker — the entry point Cloudflare Email Routing calls on every inbound message:
| |
AI extraction — Workers AI with a schema-constrained prompt:
| |
Dedup on ingestion — LinkedIn and similar senders reuse the same job URL across many alerts, so tracking params are stripped and compared against existing rows before insert:
| |
Enrichment cron — a separate Worker on an hourly schedule fills in company descriptions via Brave Search and caches them in the companies table, so the RSS feed and dashboard can join against them without hitting the API again.
Stack
Cloudflare Workers (ES modules), D1, Workers AI, Email Routing, Zero Trust, postal-mime. No build step required — Wrangler deploys plain JS directly. A short Makefile wrapping wrangler deploy and wrangler tail per worker keeps day-to-day operation simple.