Automate your job applications: email your alias (e.g. you+jobassistant@gmail.com) and this script will:
- auto-label the thread
- rename & save attachments to Drive
- extract Company and Role using the OpenAI API (JSON mode)
- log to Google Sheets (
Log,Runs,Activitytabs) - email a receipt back to the original sender (with optional CC)
Privacy note: API keys and IDs are stored in Script properties (not hardcoded). No secrets in source.
- Lean minute trigger (no full inbox scan); skips heavy work if nothing’s new
- Auto-label alias mail (tiny Gmail search)
- Deterministic file naming and year/date folders in Drive
- OpenAI extraction (structured JSON; robust fallback)
- Dedup by Gmail permalink
- Receipts & error emails (to the original sender, optional CC to you)
- Observability: three Sheets tabs
Log– canonical application tableRuns– one row per execution (counts, timing, outcome)Activity– per-thread details (optional AI prompt/response, truncated)
- Personal Google account with Gmail, Drive, Sheets
- OpenAI API key (restricted to
/v1/chat/completions→ Write) - A Google Sheet (blank is fine; script will create headers)
- No deploy/web app needed—just a time trigger
-
Create the Google Sheet
- Go to Google Sheets → Blank.
- Copy the Sheet ID (between
/d/and/editin the URL).
-
Create alias (optional but recommended)
- Use any Gmail plus-alias, e.g.
you+jobassistant@gmail.com.
- Use any Gmail plus-alias, e.g.
-
Open Apps Script
- https://script.google.com/ → New project.
- Create a
JobAssistant.gsfile and paste the contents fromsrc/JobAssistant.gs(from this repo). - Save.
-
Project Settings → Script properties (add rows; key/value):
SHEET_ID=<your sheet id>BASE_FOLDER_PATH=Job AppsALIAS_EMAIL=you+jobassistant@gmail.comNOTIFY_EMAIL=<your main email>(optional CC on receipts)USE_LLM=trueLLM_PROVIDER=OPENAIOPENAI_API_KEY=<your OpenAI key>- (optional)
DEBUG=true(console logs in Executions) - (optional)
LOG_AI_PAYLOADS=true(store AI prompt/response in Activity; setfalseto suppress) - (optional)
LOG_AI_MAXLEN=1200 - (optional)
BATCH_SIZE=30 - (optional)
ALIAS_LOOKBACK_DAYS=2
-
Authorize & initialize
- In Apps Script, pick function
setup→ Run. - Approve permissions (Gmail, Drive, Sheets, external requests).
- After run, your Google Sheet will have
Log,Runs,Activitytabs with headers.
- In Apps Script, pick function
-
Add a time trigger
- Left sidebar Triggers (⏰) → Add Trigger:
- Function:
processJobAssistant - Event source: Time-driven
- Type: Every 1 minute (or every 5)
- Function:
- Save.
- Left sidebar Triggers (⏰) → Add Trigger:
Tip: if strict Firefox privacy blocks OAuth popups, do the first Run → setup in Chrome/Edge once.
-
From any account, send an email to your alias
Subject:Applied: Sopra Steria – Observability konsulent
Body: include a job URL
Attach: two PDFs (CV + letter) -
Either wait a minute for the trigger or Run → processJobAssistant.
-
Verify:
- Drive:
Job Apps/<year>/<YYYY-MM-DD_Company_Role>/with renamed PDFs - Sheet:
Logrow (links, hashes, company, role, permalink)Runsrow (counts, duration, outcome=processedorsilent-check)Activityrow (sender, subject, links; plus AI payloads if enabled)
- Email: sender gets a receipt (optional CC to
NOTIFY_EMAIL). Errors send to sender too and label threadJobAssistant/Error.
- Drive:
- Naming: edit
guessDocType()and folder naming lines inJobAssistant.gs. - Receipt content: adjust receipt body string in
processJobAssistant()after files save. - Digest: add a weekly
digest()that queries theLogtab and emails a summary (left as an exercise).
- API key stored in Script properties; never commit keys or IDs.
- Optional logging of AI prompt/response; set
LOG_AI_PAYLOADS=falseto suppress. - Data stays in your Google account (Gmail/Drive/Sheets).
- OpenAI calls happen only when a new labeled thread is processed.
- Idle ticks (no work) perform a tiny Gmail search + quick exit.
- Nothing happens: check that the alias email received the message; ensure the time trigger exists; look at Executions for logs.
- 401/429 from OpenAI: verify
OPENAI_API_KEY; try again later for 429. - Wrong company/role: the LLM is robust; adding lines like
Company:/Position:in the email body improves reliability. - Duplicates: dedup is by Gmail permalink +
Processedlabel; forwarding as a new thread will log as a new entry (expected).
MIT — see LICENSE.
See docs/FAQ.md.