A scheduled workflow that finds remote roles worth applying to, scores them against a candidate profile, and delivers a ranked shortlist to Slack every morning.
Built on Gumloop with custom Python nodes, Claude for scoring, Notion for storage, and Slack for delivery. This repository documents the architecture, the design decisions, and the engineering behind Role Scout.
Status: v1 built end to end (ATS sources only). A short demo of a live run is in progress; the link below will be added once recorded.
Demo: coming soon
A serious remote job search means checking the same company career pages and applicant tracking systems (ATS) every day, reading past dozens of roles that don't fit, and manually judging each one against your background, location, and eligibility. It's repetitive, easy to fall behind on, and exactly the kind of work that should be automated.
Role Scout does that judgment automatically, once a day, and only surfaces the handful of roles actually worth a human look.
Every morning, on a schedule, Role Scout:
- Reads a watchlist of target companies from Notion (company, ATS provider, slug, lane).
- Fetches open roles directly from each company's ATS API (Greenhouse, Lever, Ashby, Workable, Recruitee).
- Filters by title against a three-tier keyword model, dropping anything off-target before any AI runs.
- Deduplicates against roles already seen, so the same posting is never processed twice.
- Scores each new role with Claude against a structured candidate profile: eligibility first (location, timezone, seniority), then a weighted fit score.
- Saves every scored role to a Notion database, with the score, reasoning, red flags, and a tailoring angle.
- Sends the top five eligible, high-scoring roles as a ranked digest to a Slack channel.
The result is a short, ranked, decision-ready shortlist in Slack each morning, backed by a searchable Notion database of everything that was scored.
Role Scout is a single linear pipeline. Data flows from the Notion watchlist through fetch, normalize, filter, dedupe, score, save, and digest.
Notion Watchlist (companies to check)
│
▼
ATS Source Fetcher ── custom: builds each ATS URL, GETs with browser-like
│ headers, catches failures per company, never stops the run
▼
ATS Normalizer ── custom: parses 5 different ATS response schemas into one
│ unified job shape; counts total roles scanned
▼
Tier Filter ── custom: 3-tier keyword match on job title, drops non-matches
│
▼
Dedupe Filter ── keeps only roles whose URL is not already in the database
│
▼
Batch Chunker ── custom: groups roles into batches of 10 as JSON for scoring
│
▼
Claude Scoring ── one AI call per batch (never per role); eligibility + fit
│
▼
AI Response Parser ── custom: parses batch responses, keeps field arrays aligned
│ by URL, logs and skips any batch that fails to parse
├──────────────► Notion Writer ── saves every scored role (audit trail)
│
▼
Digest Builder ── custom: filters score ≥ 7, ranks, takes top 5, formats
│
▼
Slack Message Sender ── posts the daily digest to #role-scout
Six custom Python nodes handle the logic that native workflow blocks can't: multi-schema ATS fetching, schema normalization, tiered keyword filtering, batch chunking, response parsing with alignment guarantees, and digest formatting.
ATS-first, not job boards. v1 pulls from company ATS endpoints only. Company APIs return clean, structured, stable data. Job-board aggregators overlap heavily and change often, so they are deferred to a later pass where the watchlist can be fed from them rather than scraped directly.
Eligibility before scoring. The scoring prompt rejects by default. A role is only eligible if it explicitly allows worldwide, EMEA, Africa, a GMT+1 timezone window, or a country list including the candidate's. This front-loads the hardest filter and stops the pipeline from ranking roles that were never applicable.
One AI call per batch, never per role. Roles are chunked into groups of ten and scored in a single call each. This keeps cost predictable and low regardless of how many roles a run surfaces.
Save everything, digest the best. Every scored role is written to Notion, including rejected and zero-scored ones, so the eligibility screener can be audited and tuned. The Slack digest filters to the top five, so the daily message stays short while the database stays complete.
Alignment by URL, not by position. After failed batches are dropped, every output field is rebuilt from the same surviving-roles list in one pass, keyed on URL. This guarantees a role's score, reasoning, and link always belong to the same posting.
Each role is scored in two steps:
Step 1 — Eligibility (reject by default). Location, timezone, and seniority are checked first. Anything US-only, onsite, hybrid, fixed-hours, or outside the candidate's region is marked ineligible and scored zero.
Step 2 — Fit score (eligible roles only). A weighted integer sum across evidence match, tier fit, eligibility confidence, compensation signal, and growth path. Higher scores mean a stronger, more actionable fit.
The output is strict JSON per role: URL, title, company, lane, tier, eligibility, score, a one-line reason, red flags, and a tailoring angle.
| Layer | Tool |
|---|---|
| Orchestration | Gumloop (scheduled workflow) |
| Custom logic | Python custom nodes |
| Scoring | Claude |
| Fetching | Company ATS APIs (Greenhouse, Lever, Ashby, Workable, Recruitee) |
| Storage | Notion (watchlist in, scored roles out) |
| Delivery | Slack |
- v1 (built): ATS sources, scoring, Notion storage, Slack digest.
- v2: Add API-grade job-board sources (Remotive, RemoteOK, WeWorkRemotely, Himalayas), feeding newly discovered companies back into the watchlist.
- v3: Careers-page extraction for companies without a public ATS API.
- Later: A lightweight interface to run the pipeline on demand and browse results.