HTTP API
Everything the pages do goes through this JSON API on http://localhost:8765. No
authentication - the server binds loopback only. All responses carry Cache-Control: no-store.
Jobs
Method & path
Purpose
GET /api/jobs
the list. Query: tab (inbox · viewed · starred · saved · applied · not_interested · hidden · all), q, min_score (reads COALESCE(fit, score)), loc, mode, role, source (comma lists; direct / aggregator expand), company, auth (default hides citizen-only; all, citizen_only, no_sponsorship), days + days_by (posted / updated), new=1, sort (fit · score · date · updated · source · auth). Returns jobs (duplicates collapsed, capped at 1,500), tab_counts, total, truncated.
GET /api/jobs/<id>
full row with description, breakdown, flags
POST /api/jobs/<id>/status {status}
set a status (toggle semantics live in the UI); applied stamps applied_at, viewed_at and skips an un-run plan; not_interested / hidden drop the job from both queues
POST /api/jobs/<id>/viewed, /notes
timestamps and notes
GET /api/facets
companies, roles, sources for the filter widgets
GET /api/status
version (from VERSION), counts, last-refresh report, refresh progress (running, phase, done, total, failed, started, trigger), auto_refresh (at, every_hours, next), home, view
POST /api/refresh {company?} / {companies?: []}
full or partial refresh in the background; 409 while one runs
POST /api/rescore
re-apply skills.yaml to every stored job
Fit scoring
Method & path
Purpose
GET /api/fit/status
queue, done/failed, ETA, scored/stale counts, gate candidates, profile version, pending feedback
POST /api/fit/start {job_ids?, min_score?, limit?}
queue gated jobs (given ids, or all without a score)
POST /api/jobs/<id>/fit/feedback {verdict, note}
verdict too_high / right / too_low; may trigger a profile update
GET /api/fit/profile
the current fit-profile.md, version, recent feedback
POST /api/fit/profile
fold pending feedback into the profile now; re-queues stale jobs
Presets, settings, companies
Method & path
Purpose
GET /api/presets
shipped presets and the active one
POST /api/presets/load {id}
copy a preset over skills.yaml (backup kept) and rescore
GET /api/settings, POST /api/settings
sections adzuna, filters, home, refresh, resume, apply, notify, fit; POST writes local.yaml
POST /api/settings/test_adzuna, /test_notify, /test_claude
connectivity checks
GET /api/companies
every company with cumulative stats and board config
POST /api/companies/detect {name, url?}
probe or fingerprint a company's ATS (synchronous; Workday discovery can take a minute)
POST /api/companies/add {name, ats, slug}
append to companies.yaml (validated, reverted on failure)
POST /api/companies/add_sweep {name}
add to the Adzuna company sweeps
POST /api/companies/add_watch {name, url?, platform?}
append to watchlist.yaml
GET /api/watchlist
the watchlist with live Adzuna coverage
Resume tailoring
Method & path
Purpose
POST /api/resume/start {job_ids, include_applied?, regenerate?}
queue tailoring (citizen-only, applied, ready, in-progress skipped; capped)
GET /api/resume/status
everything the Resumes tab needs: queue, workers, per-job states, open questions
POST /api/resume/answer {answers:[{job_id, skill, answer, context, scope}]}
answer analyze questions; scope: global writes the inventory
POST /api/resume/queue {job_id, action}
reorder / dequeue; POST /api/resume/pause {paused}; POST /api/resume/parallel {n}
POST /api/jobs/<id>/resume/retry, /open, /qa, /cover
retry, open the folder, generate Q&A answers, generate cover letters
GET /api/jobs/<id>/resume/notes, GET /api/jobs/<id>/apply/files
notes; the files a filler can use (resume, cover docx, cover text long/short)
Applying
Method & path
Purpose
POST /api/apply/plan {job_ids}
read the form and build plans
POST /api/jobs/<id>/apply/review {action: approve · skip · reset · fix, fixes?, mode?}
review actions
POST /api/apply/approve {job_ids}, POST /api/apply/enqueue {job_ids}, POST /api/jobs/<id>/apply/dequeue
bulk approve, queue, dequeue
POST /api/jobs/<id>/apply/draft_answers
draft the plan's essay questions
GET /api/apply/status
plans by state, queue
GET /api/apply/queue/next?mode=&runner=
filler : lock and hand out the next queued plan ({done: true} when empty)
POST /api/apply/result {run_id, outcome, reason?, screenshot?, form_hash_seen?, confirmation?}
filler : report prefilled · submitted · held · failed
POST /api/jobs/<id>/apply/mark_submitted
you submitted a prefilled form yourself
POST /api/jobs/<id>/apply/screenshot, GET …/screenshot/<kind>
filled-form screenshots
Pages
/ Jobs · /companies · /watchlist · /settings · /about.