A truthful, human-in-the-loop job-application agent. Pick a job posting, watch the agent tailor your resume, fill the form, and wait for your Telegram approval before it touches Submit.
A hand-selected applier, not a bulk applier. Target: 15–20 carefully chosen applications/day, hard cap 25. For each application, the agent:
- Detects the ATS (Greenhouse, Lever, Ashby, Workable, SmartRecruiters; falls back to a vision-driven Tier-2 loop for Workday / iCIMS / custom).
- Picks the best resume variant from your
resumes/folder and tailors it to the JD with Gemini Flash. - Validates the tailored output: every employer, year, tool, and metric must trace to your source resume. Hallucinations get rejected, regenerated, then escalated to you.
- Renders to PDF with a UUID stamped into the metadata so you can A/B response rates per variant.
- Persists the application, posts a Telegram approval card with bullet-level diffs, and waits for ✅ Submit.
- On approval, fills the form (still doesn't click Submit unless you pass
--no-dry-run). - Monitors the inbox for responses. Interview / recruiter / offer emails trigger immediate high-priority Telegram alerts with excerpt + matched application context.
It gets smarter every time you intervene. Every answer you supply through Telegram goes into a semantic qa_log and is reused (verbatim above 0.85 similarity, rephrased between 0.70 and 0.85, escalated below).
- Not a bulk applier. The pre-flight rejects past 25/day, refuses outside 10am–6pm in your local submit window, and enforces a 90-day cooldown on
(company, role). - Not a Submit-by-default tool.
dry_run=Trueis the safety default. You explicitly opt in with--no-dry-runonce you've watched a dry run. - Not a resume rewriter. The validator rejects any tailored output that introduces facts not in the source
.docx/.md— no embellishment, no fabricated metrics. - Not a LinkedIn Easy Apply bot. LinkedIn is Tier-3 (manual takeover) only. The agent prepares everything and hands you the wheel.
| Area | What you get |
|---|---|
| ATS adapters | Greenhouse, Lever, Ashby, Workable, SmartRecruiters (Tier-1 deterministic). Workday / iCIMS / custom (Tier-2 vision loop). LinkedIn (Tier-3 manual). |
| Truthfulness | Sacred validator with entity extraction (years, acronyms, proper nouns), 2-retry regenerate, then human escalation. |
| HITL | Telegram approval before every Submit. Inline buttons for ✅ Submit / ✗ Reject / 📷 Screenshot / ✏ Edit. Pause/resume on uncertainty. |
| Learning | qa_log semantic store. Local bge-small-en-v1.5 + sqlite-vec for cosine ranking. Reuse > 0.85, rephrase 0.70-0.85, pause < 0.70. |
| Email monitoring | IMAP IDLE listener. Signup-verify correlator + 4–8-digit OTP extractor. Gemini classifier into 6 response categories. High-priority alerts on interview/recruiter/offer. |
| Encryption | Per-row age encryption of portal credentials. Master key never written to logs. CSV mirror at secrets/portal_passwords.csv for human read. |
| Pre-flight | Submission window, daily cap, 90-day cooldown, salary floor, H1B sponsor pre-filter, Gemini quota headroom. |
| Observability | Per-variant response-rate dashboard. Daily 7pm-local digest. Training-run analyzer that proposes new qa_log seeds every 10 apps. |
| Backup | Nightly tar.gz of secrets/, data/, and the DB. Keeps last 30. |
| Cost | $0/mo runtime. Gemini 2.5 Flash free tier (15 RPM / 1500 RPD / 1M TPM). |
# Inside WSL2 Ubuntu (or any Linux with Python 3.11+):
git clone https://github.com/drajb/Job-Application-Automation.git apply-agent
cd apply-agent
make dev-install
cp .env.example .env # fill in keys as you get them
make keygen # creates secrets/master.age.key — BACK IT UP
cp profile.example.yaml secrets/profile.yaml
$EDITOR secrets/profile.yaml # fill in your details
make encrypt-profile # encrypts to .age, removes plaintext
make migrate
make test # 55+ tests should pass
make run # boots Telegram bot + IMAP + schedulerFrom Telegram:
/apply https://boards.greenhouse.io/<some-company>/jobs/<id>
You'll get an approval card. The agent stops at dry-run by default; pass --no-dry-run only after you've verified the tailored output is good.
See docs/QUICKSTART.md for the unhurried walkthrough.
| Risk | Mitigation |
|---|---|
| Hallucinated resume content | Sacred validator rejects unseen entities. 2-retry hard fail, then Telegram escalation. |
| Bot detection on hardened ATSes | Tier-3 manual takeover. The agent does all prep, you finish in a real browser window. |
| Telegram bot hijack | chat_id allowlist enforced on every command + callback. |
| Wrong company submission | Pre-submit screenshot + Telegram approval card with bullet diffs. Mandatory. |
| Credential leak | age-encrypted vault. Master key in secrets/master.age.key (gitignored). Decrypted only in memory after process start. |
| Captcha | Always escalate to Tier-3 takeover. No third-party solvers. |
| Phishing in verification email | Sender domain match + intentional expectation lifecycle. |
| Wrong submission window | Hard refuse outside 10am–6pm in the configured timezone. |
| Quota exhaustion | Rate tracker pauses new applications at 95% of Gemini's daily RPD. |
┌─────────────────────────────────────────────────────────────────┐
│ YOU (Telegram, mobile) │
└──────────┬──────────────────────────────────────────▲───────────┘
│ /apply <url>, /handoff, /done, etc. │ approvals,
▼ │ inbox alerts
┌─────────────────────────────────────────────────────────────┐
│ apply-agent (single Python process, src/main.py) │
│ ├─ Telegram bot (python-telegram-bot, long polling) │
│ ├─ IMAP IDLE listener (imap_tools, any IMAP server) │
│ ├─ FastAPI internal endpoints (127.0.0.1:8080) │
│ ├─ Orchestrator (apply_to) │
│ │ ├─ Resume selector → tailor → validator → renderer │
│ │ ├─ Pre-flight (window, cap, cooldown, salary, quota) │
│ │ └─ Telegram approval gate │
│ ├─ Execution router (Tier 1 / Tier 2 / Tier 3) │
│ ├─ Account vault (age + CSV mirror) │
│ ├─ Email handler (signup correlator + classifier) │
│ └─ SQLite + sqlite-vec (apply_agent.db) │
│ │
│ Local Chromium (Playwright, dedicated profile) │
│ Local bge-small-en-v1.5 (embeddings) │
│ Local LibreOffice (docx → pdf) │
└─────────────────────────────────────────────────────────────┘
Full diagram + module-by-module breakdown in docs/ARCHITECTURE.md.
All knobs are env vars. See docs/CONFIGURATION.md for the full list. Minimum to run:
| Var | Required for | Where to get it |
|---|---|---|
GEMINI_API_KEY |
Resume tailoring + classifier | https://aistudio.google.com/apikey |
TELEGRAM_BOT_TOKEN |
HITL approval | @BotFather on Telegram |
TELEGRAM_CHAT_ID |
HITL approval | getUpdates after messaging your bot |
APPLY_EMAIL_USER |
Email monitoring + signup verify | A dedicated job-application email |
APPLY_EMAIL_PASSWORD |
Email monitoring + signup verify | App password (enable 2FA first) |
APPLY_EMAIL_IMAP_HOST |
Email monitoring | Defaults to imap.gmx.com |
RESUME_SOURCE_DIR |
Resume selection | Defaults to ./resumes/ |
Missing keys cause clear startup warnings, not crashes. The affected subsystem disables itself and the rest of the agent runs.
| Target | What it does |
|---|---|
make install |
venv + runtime deps |
make dev-install |
+ pytest/ruff/mypy |
make run |
python -m src.main (polling) |
make test |
pytest |
make lint |
ruff check |
make migrate |
apply alembic migrations |
make keygen |
generate secrets/master.age.key (run ONCE; back it up) |
make encrypt-profile |
encrypt secrets/profile.yaml → .age, remove plaintext |
make passwords |
print path to secrets/portal_passwords.csv |
make clean |
wipe caches; secrets and DB untouched |
- docs/QUICKSTART.md — get from zero to your first dry-run application in 30 minutes.
- docs/SPEC.md — the locked architectural decisions. Read before opening a substantive PR.
- docs/ARCHITECTURE.md — diagrams, module boundaries, data flow.
- docs/CONFIGURATION.md — every env var and CLI flag.
- docs/ATS_SUPPORT.md — supported ATSes + how to add a new one.
- docs/PRIVACY.md — what data is stored, where, and how encryption works.
- docs/FAQ.md — common questions.
PRs welcome — especially new ATS adapters, prompt improvements, and additional pre-flight checks. See CONTRIBUTING.md for the workflow + how to add a new ATS adapter.
Found a vulnerability? See SECURITY.md for responsible disclosure.
MIT. Use it, fork it, ship it. Attribution appreciated but not required.
Built with Gemini 2.5 Flash, Playwright, python-telegram-bot, imap-tools, bge-small-en-v1.5, sqlite-vec, age, and LibreOffice headless.