Python-only Instantly-like outreach MVP built with FastAPI, PostgreSQL and Redis. Supports SMTP/IMAP providers plus Gmail/Outlook OAuth with encrypted credential storage and a simple HTMX/Jinja UI.
- Email account CRUD with STARTTLS/SSL and encrypted credentials (Fernet + MASTER_KEY).
- Campaigns with multi-step sequences, lead attachments, per-account/campaign throttling and basic planner.
- In-process APScheduler planner + sender loop with SendJob tracking (no Docker required).
- IMAP polling hook (utility) for reply/bounce detection.
- Unified Unibox view for inbound/outbound history.
- REST endpoints under
/api/*alongside server-rendered UI. - Basic auth with cookie session; default admin
admin@yassir.smtp/adminbootstrapped when DB is empty.
- Clone repo and create env file:
cp .env.example .env # set MASTER_KEY to a 32-byte base64-style value from: python - <<'PY' from cryptography.fernet import Fernet print(Fernet.generate_key().decode()) PY
- Start Postgres + Redis (optional helper):
docker-compose up -d
- Create virtual env and install deps:
python -m venv .venv source .venv/bin/activate pip install --upgrade pip pip install -r requirements.txt - Run the app + background scheduler from the single entrypoint:
This boots FastAPI on
python run.py
http://localhost:3001and starts minute-level planner/sender tasks (APScheduler) and ensures/data/leads_uploadsexists. - Open http://localhost:3001 and log in with admin credentials. Add accounts, leads, create campaign steps, attach leads, start campaign, monitor Unibox.
- Use PowerShell for env setup:
python -m venv .venv .\.venv\Scripts\Activate.ps1 pip install -r requirements.txt python run.py
- Docker Desktop can run
docker-compose up -dfor Postgres/Redis.
- Gmail: set
GMAIL_CLIENT_ID/GMAIL_CLIENT_SECRETand configure the OAuth redirect URI tohttp://localhost:3001/api/oauth/google/callback. - Outlook: set
OUTLOOK_CLIENT_ID/OUTLOOK_CLIENT_SECRETwith redirect URIhttp://localhost:3001/api/oauth/outlook/callback. Tokens are stored encrypted (MASTER_KEY) and auto-refreshed via the provider abstractions.
- CSV must include
emailcolumn, optionalfirst_name,last_name,company. - Upload saves to
/data/leads_uploads/YYYYMMDD_HHMMSS_original.csv; preview first 10 rows before import. - Import is idempotent: duplicates update blank fields, track stats in
lead_importstable.
Alembic migration lives in backend/app/alembic/versions/0001_init.py.
Run upgrades:
alembic -c backend/app/alembic.ini upgrade head- Add account with correct host/port/security (SSL or STARTTLS) and username/password.
- Use provider app-passwords when needed (Gmail/Outlook).
- All sends are individual (no BCC batching). Template variables:
{{first_name}},{{last_name}},{{company}},{{email}}.
backend/app/main.py: FastAPI app, UI routes, REST stubs.run.py: entrypoint launching FastAPI + APScheduler.backend/app/workers.py: planner/sender helpers forSendJobs.backend/app/providers.py: provider abstraction for SMTP/Gmail/Outlook.backend/app/smtp_client.py: SMTP sender usingaiosmtplib.backend/app/imap_client.py: IMAP polling helper.backend/app/crypto.py: Fernet-based encryption usingMASTER_KEY.backend/app/models.py: SQLAlchemy 2.0 models for users, accounts, campaigns, steps, leads, jobs, messages, suppression list.
- Credentials encrypted with Fernet (AES-128 in practice; provide strong MASTER_KEY).
- Session cookies are signed using
JWT_SECRET. - Avoid sending without consent; default lead consent is opt-in in UI.