Automated B2B outreach pipeline: Apollo → Supabase → Gmail drafts → Gemini AI personalization.
- Apollo poller (every 4h) — fetches new leads matching your ICP filters
- Gemini generator — creates personalized cold email drafts for each lead
- Gmail draft creator — pushes the draft directly into your Gmail inbox
- Sent detector (every 1-2 min) — detects when you send a draft and marks the lead as
contacted - Reply detector (every 5 min) — detects inbound replies and notifies via Slack
- Follow-up scheduler (daily) — auto-creates follow-up drafts for non-replies
- Node.js 24+
- npm
npm installcp .env.example .envFill in all values in .env:
| Variable | Description |
|---|---|
APOLLO_API_KEY |
Master API key from Apollo settings. A regular key returns 403 — it must be a master key. |
SUPABASE_URL |
Your Supabase project URL (https://<ref>.supabase.co) |
SUPABASE_SERVICE_ROLE_KEY |
Service role key from Supabase project settings |
SUPABASE_ANON_KEY |
Anon key from Supabase project settings |
GEMINI_API_KEY |
API key from Google AI Studio |
GMAIL_CLIENT_ID |
Google OAuth 2.0 client ID |
GMAIL_CLIENT_SECRET |
Google OAuth 2.0 client secret |
GMAIL_REFRESH_TOKEN |
Generated by running npm run gmail:auth |
SLACK_WEBHOOK_URL |
Incoming webhook URL from your Slack app |
ADMIN_API_TOKEN |
Secret token for the admin override endpoint |
npm run devnpm run gmail:authFollow the browser prompt. Copy the printed refresh token into GMAIL_REFRESH_TOKEN in .env.
| Script | Description |
|---|---|
npm run dev |
Run the worker with hot-reload via tsx |
npm run build |
Compile TypeScript to dist/ |
npm run typecheck |
Type-check without emitting files |
npm run lint |
Run ESLint on src/ |
npm run lint:fix |
Auto-fix ESLint issues |
npm run format |
Format src/ with Prettier |
npm run format:check |
Check formatting (used in CI) |
npm run eval:emails |
Generate 20 sample Gemini emails (Atelierra voice, EN/DE based on lead country) and write a reviewer doc to docs/eval-results-*.md (AVI-17 quality gate) |
Leads have a nullable country column (ISO-2 code, e.g. DE, AT, CH, GB). Outreach language is derived in code via detectLanguageFromCountry:
DE,AT,CH→ German- Anything else (or null) → English
The active Supabase template (Default B2B Outreach) is still placeholder-based and unchanged in this issue. The Atelierra voice and language switching live in the Gemini prompt (src/integrations/geminiClient.ts) and the eval fixture (src/scripts/evaluateEmails.ts). They will be wired into the live pipeline by AVI-18.
src/
jobs/ # Cron jobs and schedulers
integrations/ # Apollo, Gmail, Gemini, Slack API clients
db/ # Supabase client and query helpers
lib/ # Shared utilities (retry, logging, etc.)
index.ts # Main worker entrypoint
supabase/
migrations/ # Versioned SQL migrations
docs/ # Spike documents and runbooks
.github/
workflows/ # GitHub Actions CI