| title | Newsletter Digest API |
|---|---|
| emoji | 🚀 |
| colorFrom | blue |
| colorTo | indigo |
| sdk | docker |
| pinned | false |
Pull recent newsletters from your Gmail, AI-filter them, then generate three text outputs:
- Digest — themed summary of every newsletter in the window
- Story — narrative version of the digest
- LinkedIn post — short post derived from the story
Single-user: no auth, just don't share the URL. Email + IMAP password + lookback hours + prompts persist in your browser. Past runs persist in Supabase, scoped by the email you entered.
- Backend — FastAPI on Render (
backend/) - Frontend — Next.js on Vercel (
frontend/) - DB — Supabase Postgres (one
runstable) - LLM — OpenRouter, model
meta-llama/llama-3.3-70b-instruct:free
- A Gmail account with an App Password (16 chars). Generate at https://myaccount.google.com/apppasswords. You need 2-Step Verification on first.
- Accounts on: Supabase, Render, Vercel, OpenRouter.
- Create a project. Note the Project URL and the service_role key (Settings → API). The service key is secret — never put it in the frontend.
- Open the SQL Editor and run the contents of
supabase_schema.sql.
This repo includes render.yaml — Render will pick it up automatically.
- Push the repo to GitHub.
- Render → New → Blueprint, point at the repo. It will detect
render.yamland create thenewsletter-digest-apiweb service. - Set these Environment variables on the service:
OPENROUTER_API_KEY— your OpenRouter keySUPABASE_URL—https://xxxx.supabase.coSUPABASE_SERVICE_KEY— the service_role key from step 1ALLOWED_ORIGINS— your Vercel URL (e.g.https://newsletter-digest.vercel.app). Use*only while testing.
- Deploy. After it's up, hit
https://<your-service>.onrender.com/— should return{"ok": true, ...}.
Render free tier sleeps after ~15 min idle. The first request after a sleep takes ~30s to wake. Subsequent runs are fast.
- Vercel → Import Project → pick the repo.
- Root Directory:
frontend - Environment variable:
NEXT_PUBLIC_API_BASE=https://<your-service>.onrender.com
- Deploy.
After Vercel gives you a URL, go back to Render and set ALLOWED_ORIGINS to that URL,
then redeploy the backend so CORS picks it up.
Open the Vercel URL.
- Enter your Gmail address and 16-char app password.
- Click Verify connection — confirms IMAP login works.
- Set hours-back (default 24). Optionally edit prompts under "Prompts (advanced)".
- Click Run. Spinner runs while the pipeline:
- fetches mails from the last N hours across all folders
- asks the LLM (one batched call) which are newsletters
- aggregates the kept bodies
- generates digest → story → LinkedIn post
- Results show in tabs. Past runs appear at the bottom and persist in Supabase.
After the Make.com LinkedIn webhook is working manually:
- In Settings, set the lookback window, then enable Auto-post daily.
- Pick the daily post time and timezone.
- Click Save Settings.
- Configure the Render cron job from
render.yamlwith:AUTOMATION_TICK_URL=https://<your-service>.onrender.com/automation/tickCRON_SECRET= the same value as the backend service, if you set one
The cron job pings the backend every 5 minutes. The tick endpoint checks all enabled
users, runs only users whose local scheduled time has passed, uses each user's saved
lookback window, and records last_run_at so each user runs at most once per local day.
Optional: set CRON_SECRET on the backend and send it as the X-Cron-Secret header
from your scheduler. If CRON_SECRET is not set, the tick endpoint is open but still
only runs saved schedules that are due.
# Backend
cd backend
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env # fill in keys
# load env then run:
uvicorn main:app --reload --port 8000
# Frontend (separate terminal)
cd frontend
npm install
echo "NEXT_PUBLIC_API_BASE=http://localhost:8000" > .env.local
npm run dev- The OpenRouter free model has rate limits. Each run makes 4 LLM calls (filter, digest, story, LinkedIn). The backend retries on 429/5xx with exponential backoff.
- All errors surface verbatim to the UI (red banner). No silent fallbacks.
- Mail bodies > 8000 chars are truncated per-mail; the aggregate is capped at 80k chars to fit context.
- "Newsletter" detection is purely the filter prompt — edit it in the UI to taste.