docs: cut version 0.1.28

This commit is contained in:
github-actions[bot] 2026-02-25 21:39:35 +00:00
parent f234f8005c
commit 65c558ffe4
32 changed files with 2963 additions and 1 deletions

View File

@ -0,0 +1,66 @@
---
id: adzuna
title: Adzuna Extractor
description: API-based Adzuna extraction with orchestrator ingestion and progress updates.
sidebar_position: 6
---
## What it is
Original website: [adzuna.com](https://www.adzuna.com)
Adzuna is an API-backed extractor implemented in two lean pieces:
1. `extractors/adzuna/src/main.ts` fetches paginated Adzuna search results and writes `jobs.json`.
2. `orchestrator/src/server/services/adzuna.ts` runs the extractor, parses progress lines, and maps rows into `CreateJobInput`.
It de-duplicates in the existing repository path using `sourceJobId` fallback to `jobUrl`.
## Why it exists
Adzuna provides stable API discovery for countries that are not covered by UK-only sources. It adds a lower-maintenance source without introducing new API routes or UI sections.
## How to use it
1. Create an Adzuna developer account.
2. Open [Adzuna Access Details](https://developer.adzuna.com/admin/access_details).
3. Copy your **App ID** and **App Key**.
4. In Job Ops, open **Settings** and paste them into `Adzuna App ID` and `Adzuna App Key` under **Environment & Accounts**.
5. In **Pipeline Run** (Automatic tab), select a compatible country and enable **Adzuna** in Sources.
6. Start the run; Adzuna progress appears in the existing crawl progress stream.
City behavior:
- If **Search cities** are set in Automatic advanced settings, Adzuna runs once per city.
- City runs use strict post-filtering (`job.location` contains requested city) to avoid broad country-level spillover.
Default controls:
- `ADZUNA_APP_ID`
- `ADZUNA_APP_KEY`
- `ADZUNA_MAX_JOBS_PER_TERM` (default `50`)
- `ADZUNA_LOCATION_QUERY` (optional city/location text)
Supported countries in this integration:
- United Kingdom, United States, Austria, Australia, Belgium, Brazil, Canada, Switzerland, Germany, Spain, France, India, Italy, Mexico, Netherlands, New Zealand, Poland, Singapore, South Africa.
## Common problems
### Adzuna is disabled in source selection
- `Adzuna App ID` and `Adzuna App Key` are missing from Settings (or env).
### Adzuna is skipped for my selected country
- The selected country is not in the supported list above.
### Adzuna fails with authorization errors
- Verify `ADZUNA_APP_ID` and `ADZUNA_APP_KEY` are valid and active in your Adzuna account.
## Related pages
- [Extractors Overview](/docs/next/extractors/overview)
- [Pipeline Run](/docs/next/features/pipeline-run)
- [Settings](/docs/next/features/settings)

View File

@ -0,0 +1,48 @@
---
id: gradcracker
title: Gradcracker Extractor
description: How the Gradcracker crawler builds search URLs and extracts jobs.
sidebar_position: 2
---
A plain-English walkthrough of the Gradcracker extractor in `extractors/gradcracker`.
Original website: [gradcracker.com](https://www.gradcracker.com)
## Big picture
The crawler builds search URLs, scrapes listing pages, then opens job details for descriptions and apply URLs.
## 1) Build search URLs
- Combines UK regions with role terms.
- Defaults include roles such as `web-development` and `software-systems`.
- `GRADCRACKER_SEARCH_TERMS` overrides defaults.
## 2) Crawl list pages
- Waits for job cards (`article[wire:key]`).
- Extracts title, employer, discipline, deadline, salary, location, degree, start date.
- Queues job detail pages.
Controls:
- `GRADCRACKER_MAX_JOBS_PER_TERM`
- `JOBOPS_SKIP_APPLY_FOR_EXISTING=1`
- `JOBOPS_EXISTING_JOB_URLS` / `JOBOPS_EXISTING_JOB_URLS_FILE`
## 3) Crawl detail pages
- Waits for `.body-content`
- Captures full description text
- Clicks apply button to resolve final application URL
- Handles popup and same-tab redirects
## 4) Progress reporting
Set `JOBOPS_EMIT_PROGRESS=1` for structured progress lines consumable by orchestrator UI.
## Notes
- Uses Playwright + Crawlee via Camoufox.
- Low concurrency and longer timeouts for stability.

View File

@ -0,0 +1,77 @@
---
id: hiring-cafe
title: Hiring Cafe Extractor
description: Browser-backed Hiring Cafe extraction integrated into the pipeline source selector.
sidebar_position: 7
---
## What it is
Original website: [hiring.cafe](https://hiring.cafe)
Special thanks: Initial implementation inspiration came from [umur957/hiring-cafe-job-scraper](https://github.com/umur957/hiring-cafe-job-scraper).
Hiring Cafe is a browser-backed extractor that queries Hiring Cafe search APIs and maps results into the orchestrator `CreateJobInput` shape.
Implementation split:
1. `extractors/hiringcafe/src/main.ts` builds search state, calls Hiring Cafe APIs, and writes dataset JSON.
2. `orchestrator/src/server/services/hiring-cafe.ts` runs the extractor, streams progress events, and maps rows for pipeline import.
## Why it exists
Hiring Cafe adds another non-credentialed source that can be enabled from the existing source picker, without adding new settings UI.
It also supports term-by-term search and country-aware search state using the same pipeline knobs you already set for automatic runs.
## How to use it
1. Open **Run jobs** and choose **Automatic**.
2. **Hiring Cafe** is enabled by default in **Sources** (toggle it off if you do not want it for this run).
3. Set your existing automatic run knobs:
- `searchTerms` drive per-term Hiring Cafe `searchQuery`.
- selected country maps into Hiring Cafe location search state.
- run budget path (`jobspyResultsWanted`) is reused as the max jobs-per-term cap.
- optional **Search cities** narrow results by city.
4. Start the run and watch progress in the pipeline progress card.
Defaults and constraints:
- No new Hiring Cafe settings fields were added.
- `worldwide` and `usa/ca` run in broad mode without a strict country location filter.
- Hiring Cafe is enabled by default in source selection.
- `HIRING_CAFE_DATE_FETCHED_PAST_N_DAYS` controls recency window when running extractor directly (default `7`).
- When a city is provided via `searchCities`, Hiring Cafe uses city radius search (default `1` mile) and strict city post-filtering.
- City geocoding is resolved through Nominatim (OpenStreetMap data); if you scale extractor traffic, add attribution and cache repeated city lookups.
Local run example:
```bash
HIRING_CAFE_SEARCH_TERMS='["backend engineer"]' \
HIRING_CAFE_COUNTRY='united kingdom' \
HIRING_CAFE_MAX_JOBS_PER_TERM='50' \
npm --workspace hiringcafe-extractor run start
```
## Common problems
### Hiring Cafe returns 429 / Vercel security checkpoint
- The extractor first attempts Camoufox-backed Firefox and falls back to vanilla Firefox startup if Camoufox is unstable locally.
- If upstream blocks continue, retry later or reduce run concurrency at the pipeline level by selecting fewer sources.
### Hiring Cafe does not appear in sources
- Check that client is running on latest build containing the new source list.
- Hiring Cafe is source-only and does not require credentials, so it should appear once the new build is loaded.
### Results are lower than expected
- Cap is tied to automatic run budget path (`jobspyResultsWanted`) and search term count.
- Country mapping can narrow results when a strict country location is applied.
## Related pages
- [Extractors Overview](/docs/next/extractors/overview)
- [Pipeline Run](/docs/next/features/pipeline-run)
- [Settings](/docs/next/features/settings)

View File

@ -0,0 +1,52 @@
---
id: jobspy
title: JobSpy Extractor
description: How the JobSpy Python wrapper is orchestrated and normalized.
sidebar_position: 3
---
A walkthrough of the JobSpy extractor for Indeed, LinkedIn, and Glassdoor.
Original websites:
- [indeed.com](https://www.indeed.com)
- [linkedin.com/jobs](https://www.linkedin.com/jobs)
- [glassdoor.com](https://www.glassdoor.com)
## Big picture
JobSpy runs as a Python script per search term, writes JSON, then orchestrator ingests and normalizes into internal job shape.
## 1) Inputs and defaults
Key environment variables:
- `JOBSPY_SITES` (default: `indeed,linkedin`)
- `JOBSPY_SEARCH_TERM` (default: `web developer`)
- `JOBSPY_LOCATION` (default: `UK`)
- `JOBSPY_RESULTS_WANTED` (default: `200`)
- `JOBSPY_HOURS_OLD` (default: `72`)
- `JOBSPY_COUNTRY_INDEED` (default: `UK`)
- `JOBSPY_LINKEDIN_FETCH_DESCRIPTION` (default: `true`)
## 2) Orchestrator flow
The service in `orchestrator/src/server/services/jobspy.ts`:
- Builds search-term list from UI or env
- Runs Python once per term with unique output file
- Reads JSON and maps to `CreateJobInput`
- De-dupes by `jobUrl`
- Deletes temp output files best-effort
## 3) Mapping and cleanup
- Normalizes salary ranges
- Converts empty values to null
- Keeps metadata like skills, ratings, remote flags when available
- Skips rows with invalid site or missing URL
## Notes
- `JOBSPY_SEARCH_TERMS` can be JSON array or `|`, comma, newline-delimited text.
- Set `JOBSPY_LINKEDIN_FETCH_DESCRIPTION=0` to speed runs.
- Temp output files are stored under `data/imports/`.

View File

@ -0,0 +1,61 @@
---
id: manual
title: Manual Import Extractor
description: Import jobs from pasted descriptions and run AI-assisted inference.
sidebar_position: 4
---
Manual import lets users add jobs that automated scrapers miss.
## Big picture
User pastes raw description, AI infers structure, user reviews edits, then import saves and scores the job.
## 1) Input
Manual import accepts:
- plain text job descriptions
- raw HTML job descriptions
- job links/URLs
When a URL is provided, backend fetch attempts depend on whether the page can be resolved with `curl`. Some job sites block or heavily script content, so certain links will not resolve cleanly.
## 2) AI inference
Endpoint:
- `POST /api/manual-jobs/infer`
Service:
- `orchestrator/src/server/services/manualJob.ts`
Behavior:
- Converts the provided input into text context and sends it to the configured LLM
- Extracts structured fields (title, employer, location, salary, etc.)
- Returns inferred JSON for user review
Practical limit:
- The inference quality ceiling is mostly the configured model capability and context behavior. Better model quality generally yields better field extraction.
If no LLM key is configured, inference is skipped and user can fill fields manually.
## 3) Review and edit
User reviews inferred fields and corrects missing/wrong values.
## 4) Storage and scoring
Import endpoint:
- `POST /api/manual-jobs/import`
On import:
- Generates unique job ID if URL absent
- Stores source as `manual`
- Triggers async suitability scoring
- Persists score and reason

View File

@ -0,0 +1,42 @@
---
id: overview
title: Extractors Overview
description: Technical index of supported extractors and how they work.
sidebar_position: 1
---
This page helps you choose the right extractor for your run, understand key constraints, and navigate to detailed technical guides.
Extractor integrations are now registered through manifests and loaded automatically at orchestrator startup. Runtime discovery only scans `extractors/*/(manifest.ts|src/manifest.ts)` and does not read manifests from `orchestrator/**`. Extractor-specific run logic should also remain in `extractors/<name>/` so orchestrator stays source-agnostic. To add a new source, follow [Add an Extractor](/docs/next/workflows/add-an-extractor).
## Extractor chooser
| Extractor | Best use case | Core constraints/dependencies | Notable controls | Output/behavior notes |
| --- | --- | --- | --- | --- |
| [Gradcracker](/docs/next/extractors/gradcracker) | UK graduate roles from Gradcracker | Crawling stability depends on page structure and anti-bot behavior; tuned for low concurrency | `GRADCRACKER_SEARCH_TERMS`, `GRADCRACKER_MAX_JOBS_PER_TERM`, `JOBOPS_SKIP_APPLY_FOR_EXISTING` | Scrapes listing metadata, then detail pages and apply URL resolution |
| [JobSpy](/docs/next/extractors/jobspy) | Multi-source discovery (Indeed, LinkedIn, Glassdoor) | Requires Python wrapper execution per term; source availability and quality vary by site/location | `JOBSPY_SITES`, `JOBSPY_SEARCH_TERMS`, `JOBSPY_RESULTS_WANTED`, `JOBSPY_HOURS_OLD`, `JOBSPY_LINKEDIN_FETCH_DESCRIPTION` | Produces JSON per term, then orchestrator normalizes and de-duplicates by `jobUrl` |
| [Adzuna](/docs/next/extractors/adzuna) | API-based multi-country discovery with low scraping overhead | Requires valid App ID/App Key; country must be in Adzuna-supported list | `ADZUNA_APP_ID`, `ADZUNA_APP_KEY`, `ADZUNA_MAX_JOBS_PER_TERM` | API pagination to dataset output; orchestrator maps progress and de-duplicates by `sourceJobId`/`jobUrl` |
| [Hiring Cafe](/docs/next/extractors/hiring-cafe) | Browser-backed discovery using Hiring Cafe search APIs | Subject to upstream anti-bot checks; uses browser context and encoded search-state payloads | `HIRING_CAFE_SEARCH_TERMS`, `HIRING_CAFE_COUNTRY`, `HIRING_CAFE_MAX_JOBS_PER_TERM`, `HIRING_CAFE_DATE_FETCHED_PAST_N_DAYS` | Uses existing pipeline term/country/budget knobs and maps directly to normalized jobs |
| [UKVisaJobs](/docs/next/extractors/ukvisajobs) | UK visa sponsorship-focused roles | Requires authenticated session and periodic token/cookie refresh | `UKVISAJOBS_EMAIL`, `UKVISAJOBS_PASSWORD`, `UKVISAJOBS_MAX_JOBS`, `UKVISAJOBS_SEARCH_KEYWORD` | API pagination + dataset output; orchestrator de-dupes and may fetch missing descriptions |
| [Manual Import](/docs/next/extractors/manual) | One-off jobs not covered by scrapers | Inference quality depends on model/provider and input quality; some URLs cannot be fetched reliably | App/API endpoints (`/api/manual-jobs/infer`, `/api/manual-jobs/import`) | Accepts text/HTML/URL, runs inference, then saves and scores job after review |
## Which extractor should I use?
- Use **JobSpy** for broad first-pass sourcing across common boards.
- Use **Adzuna** when you want API-first discovery in supported non-UK markets.
- Use **Hiring Cafe** when you want another term/country-driven source without adding credentials.
- Use **Gradcracker** when targeting graduate pipelines in the UK.
- Use **UKVisaJobs** for sponsorship-specific UK searches.
- Use **Manual Import** when you already have a specific posting and need direct import.
Many runs combine sources: broad discovery first, then manual import for high-priority jobs that scraping misses.
## Related extractor docs
- [Gradcracker](/docs/next/extractors/gradcracker)
- [JobSpy](/docs/next/extractors/jobspy)
- [Adzuna](/docs/next/extractors/adzuna)
- [Hiring Cafe](/docs/next/extractors/hiring-cafe)
- [UKVisaJobs](/docs/next/extractors/ukvisajobs)
- [Manual Import](/docs/next/extractors/manual)
- [Add an Extractor](/docs/next/workflows/add-an-extractor)

View File

@ -0,0 +1,75 @@
---
id: ukvisajobs
title: UKVisaJobs Extractor
description: Authenticated session flow, API pagination, and orchestrator ingestion.
sidebar_position: 5
---
UKVisaJobs is the most complex extractor because authenticated sessions are required.
Original website: [my.ukvisajobs.com](https://my.ukvisajobs.com)
## Big picture
Two layers:
1. `extractors/ukvisajobs/src/main.ts` handles login/API calls and dataset output.
2. `orchestrator/src/server/services/ukvisajobs.ts` executes extractor and ingests/de-dupes output.
## 1) Authentication and session cache
Session cache file:
- `extractors/ukvisajobs/storage/ukvisajobs-auth.json`
Flow:
- Reuse cached token/cookies when valid
- Re-login with Playwright + Camoufox when needed
- Refresh and retry on token-expired responses
Force refresh:
- `UKVISAJOBS_REFRESH_ONLY=1`
## 2) API requests
Endpoint:
- `https://my.ukvisajobs.com/ukvisa-api/api/fetch-jobs-data`
Each request includes auth token + session cookies and paginates (15 jobs/page).
## 3) Mapping
- Normalizes salary from min/max/interval
- Builds fallback visa description when content missing
- Maps `job_link` to both `jobUrl` and `applicationLink`
## 4) Output dataset
Written to:
- `extractors/ukvisajobs/storage/datasets/default/`
Includes per-job JSON files and combined `jobs.json`.
## 5) Orchestrator flow
- Spawns extractor (`npx tsx src/main.ts`)
- Runs terms sequentially with delay
- De-dupes by `sourceJobId` (fallback `jobUrl`)
- Fetches detail pages when descriptions are too short
## Controls
- `UKVISAJOBS_EMAIL`, `UKVISAJOBS_PASSWORD`
- `UKVISAJOBS_HEADLESS`
- `UKVISAJOBS_MAX_JOBS` (default 50, max 200)
- `UKVISAJOBS_SEARCH_KEYWORD`
## Practical notes
- Deleting auth cache forces next run to re-login.
- Low-concurrency/polite scraping by design.
- If extractor breaks, check session refresh path first.

View File

@ -0,0 +1,91 @@
---
id: ghostwriter
title: Ghostwriter
description: Context-aware per-job AI chat assistant behavior and API surface.
sidebar_position: 2
---
## What it is
Ghostwriter is the per-job AI chat assistant in JobOps.
Ghostwriter uses:
- current job description and metadata
- reduced profile snapshot
- global writing style settings
The UI behavior is one persistent conversation per job, shown in the right-side drawer from job details.
## Why it exists
Ghostwriter helps you produce job-specific writing quickly while preserving consistency with your profile and style settings.
Typical use cases:
- role-specific answer drafting
- cover letter and outreach drafts
- interview prep tied to the job description
- rephrasing with tone constraints
## How to use it
1. Open a job in `discovered` or `ready`.
2. Open the Ghostwriter drawer.
3. Enter your prompt and stream a response.
4. Stop or regenerate responses when needed.
### Writing style settings impact
Global settings affecting generations:
- `Tone`
- `Formality`
- `Constraints`
- `Do-not-use terms`
Defaults:
- Tone: `professional`
- Formality: `medium`
- Constraints: empty
- Do-not-use terms: empty
### Context and safety model
- Job snapshot is truncated to fit prompt budget.
- Profile snapshot includes relevant slices only.
- System prompt enforces read-only assistant behavior.
- Logging stores metadata, not full prompt/response dumps.
### API surface
- `GET /api/jobs/:id/chat/messages`
- `POST /api/jobs/:id/chat/messages` (streaming)
- `POST /api/jobs/:id/chat/runs/:runId/cancel`
- `POST /api/jobs/:id/chat/messages/:assistantMessageId/regenerate` (streaming)
Compatibility thread endpoints remain, but UI behavior is one thread per job.
## Common problems
### Responses feel too generic
- Verify the job description is complete and current.
- Confirm style constraints in Settings are specific enough.
### Generation quality is lower than expected
- Check model/provider configuration in Settings.
- Tighten prompts with explicit output intent (for example, "3 bullet points for recruiter outreach").
### Missing context in answers
- Update profile data and relevant project details used by Ghostwriter context.
- Regenerate after updating job notes/description.
## Related pages
- [Settings](/docs/next/features/settings)
- [Reactive Resume](/docs/next/features/reactive-resume)
- [Orchestrator](/docs/next/features/orchestrator)

View File

@ -0,0 +1,92 @@
---
id: in-progress-board
title: In Progress Board
description: Post-application kanban board for tracking fewer, higher-attention jobs through interview and offer stages.
sidebar_position: 3
---
## What it is
The In Progress Board is a kanban view for jobs that have moved beyond initial application.
![In Progress Board kanban lanes](/img/features/in-progress-board.png)
It groups jobs into post-application lanes:
- Recruiter Screen
- Assessment
- Team Match
- Technical Interview
- Final Round
- Offer
- Closed
## Why it exists
JobOps uses two operational modes:
- **Pre-application tracking** (`Jobs` page): large volume, pipeline and readiness focused.
- **Post-application tracking** (`In Progress Board`): smaller volume, higher attention per job.
Once a job enters the post-application phase, each opportunity usually needs tighter follow-up, interview prep, and deliberate stage management. A kanban board is better for that than a large list.
## How to use it
1. Open **In Progress Board**.
2. Review jobs by lane to see current stage distribution.
3. Drag a card to a new lane to log a stage transition.
4. Open a card to view full job details and timeline.
5. Use sorting (Recent / Title / Company) to prioritize review.
### Moving jobs into post-application
Jobs typically move into post-application tracking when they receive a response after applying.
This can happen via:
- Tracking Inbox review/automation (recommended)
- Manual stage transitions in job detail/timeline tools
### API examples
```bash
# List in-progress jobs
curl "http://localhost:3001/api/jobs?status=in_progress&view=list"
```
```bash
# Move a job to technical interview
curl -X POST "http://localhost:3001/api/jobs/<jobId>/stage-events" \
-H "content-type: application/json" \
-d '{
"toStage": "technical_interview",
"metadata": {
"actor": "user",
"eventType": "status_update",
"eventLabel": "Moved to Technical Interview"
}
}'
```
## Common problems
### Board is empty
- Confirm jobs have status `in_progress`.
- Confirm stage events exist for applied jobs expected on the board.
### A card appears in an unexpected lane
- The board uses the latest stage event as source of truth.
- Check timeline events for out-of-order or mistaken transitions.
### Drag-and-drop move failed
- Network/API error can roll back optimistic UI movement.
- Retry move and check server logs for validation errors.
## Related pages
- [Overview](/docs/next/features/overview)
- [Orchestrator](/docs/next/features/orchestrator)
- [Post-Application Tracking](/docs/next/features/post-application-tracking)

View File

@ -0,0 +1,77 @@
---
id: job-search-bar
title: Job Search Bar
description: Use the global job search/command bar to find and open jobs fast, with optional status locking.
sidebar_position: 3
---
The Job Search Bar is the quickest way to jump to any job from the Jobs page.
![Job search command bar](/img/features/job-search-bar.png)
## Open it
Use either:
- keyboard shortcut: `Cmd+K` (macOS) or `Ctrl+K` (Windows/Linux)
- the **Search** button in the Jobs page filter row
## What it searches
Search matches job fields with fuzzy ranking:
- title
- company/employer
- location
By default, very low-relevance matches are hidden so results stay focused on likely intent.
Results are grouped by status sections:
- Ready
- Discovered
- Applied
- Other
Selecting a result opens that job in its correct tab automatically.
## Status lock (`@`)
You can lock the search scope to one status:
1. Type `@` plus a status prefix (example: `@rea`, `@app`).
2. Press `Tab` or `Enter` to apply the lock.
3. Continue typing your normal query.
Supported lock targets:
- `ready`
- `discovered`
- `applied`
- `skipped`
- `expired`
Common aliases:
- `ready`: `ready`, `rdy`
- `discovered`: `discovered`, `discover`, `disc`
- `applied`: `applied`, `apply`, `app`
- `skipped`: `skipped`, `skip`, `skp`
- `expired`: `expired`, `expire`, `exp`
## Lock controls
- `Backspace` on an empty query clears the active lock.
- `Esc` clears the active lock while the search dialog stays open.
- Closing the dialog resets lock state.
## When to use this vs filters
- Use **Search Bar** when you already know what role/company you want to jump to quickly.
- Use **Filters** when you want broad narrowing (source, salary, sponsor, sort) for browsing.
## Related pages
- [Orchestrator](./orchestrator)
- [Pipeline Run](./pipeline-run)
- [Find Jobs and Apply Workflow](../workflows/find-jobs-and-apply-workflow)

View File

@ -0,0 +1,79 @@
---
id: keyboard-shortcuts
title: Keyboard Shortcuts
description: Complete keyboard shortcut reference for the Jobs page, including tab-scoped actions and help/search toggles.
sidebar_position: 4
---
This page documents keyboard shortcuts available on the Jobs page.
## Open shortcut help
Use `?` to toggle the keyboard shortcut dialog.
The dialog:
- shows shortcuts for your current tab context
- groups shortcuts by Navigation, Actions, Tabs, and General
- is shown automatically once for first-time users
## Bottom shortcut hint bar
On desktop (`lg+`), hold `Control` to reveal the bottom shortcut hint bar.
It shows the same tab-scoped shortcuts in a compact layout.
## Global navigation shortcuts
- `j` or `ArrowDown`: next job
- `k` or `ArrowUp`: previous job
- `1`: Ready tab
- `2`: Discovered tab
- `3`: Applied tab
- `4`: All Jobs tab
- `ArrowLeft`: previous tab
- `ArrowRight`: next tab
## Search and help shortcuts
- `Cmd+K` / `Ctrl+K`: open job search bar
- `/`: open job search bar
- `?`: toggle keyboard shortcut help dialog
For search lock behavior (`@ready`, `@app`, etc.), see [Job Search Bar](./job-search-bar).
## Context action shortcuts
### Available in `discovered` and `ready`
- `s`: skip job
### Available in `discovered`
- `r`: move to Ready
Note: if you have multi-select active, `r` runs bulk move-to-ready.
### Available in `ready`
- `a`: mark applied
- `p`: view PDF
- `d`: download PDF
### Available in all tabs (when applicable)
- `o`: open job listing
- `x`: toggle select current job
- `Esc`: clear selection
## Shortcut availability rules
Shortcuts are disabled while blocking modals are open (for example Run modal, Filters, drawer states).
Search (`/`) and Help (`?`) can still open their own dialogs when other blocking dialogs are not active.
## Related pages
- [Job Search Bar](./job-search-bar)
- [Orchestrator](./orchestrator)
- [Pipeline Run](./pipeline-run)

View File

@ -0,0 +1,63 @@
---
id: multi-select-and-bulk-actions
title: Multi-Select and Bulk Actions
description: Select multiple jobs with mouse or keyboard and run bulk actions like Move to Ready, Skip, or Recalculate match.
sidebar_position: 5
---
Multi-select lets you process many jobs at once instead of repeating the same action one-by-one.
## Why this exists
When you run discovery at scale, you often need to:
- skip batches of low-priority jobs
- move a shortlist to Ready quickly
- recalculate fit scores after settings/profile changes
Bulk actions reduce repetitive clicks, keep momentum high, and make triage runs faster.
## Mouse workflow
### Select jobs
1. Use the checkbox on each row to include/exclude a job.
2. Use **Select all filtered** to select jobs currently visible in the active filtered list.
3. Check the selected count in the list header.
### Run bulk actions
When one or more jobs are selected, a floating action bar appears at the bottom.
Available actions depend on selected job statuses:
- **Move to Ready**
- **Skip selected**
- **Recalculate match**
- **Clear** (clears selection)
## Keyboard workflow
Use shortcuts from the Jobs page:
- `x`: toggle select on the currently focused job
- `Esc`: clear current selection
- `r`: in `discovered`, move to Ready
`r` behavior with selection:
- if you have a multi-selection active, `r` runs the bulk **Move to Ready** action
- if nothing is selected, `r` runs move-to-ready for the single current job
## Important limits and behavior
- Bulk actions are capped at **100 jobs per run**.
- `Select all filtered` also respects the same 100-job cap.
- Selection resets when you switch tabs.
- If jobs disappear from the active filtered list, they are removed from selection automatically.
## Related pages
- [Keyboard Shortcuts](./keyboard-shortcuts)
- [Orchestrator](./orchestrator)
- [Find Jobs and Apply Workflow](../workflows/find-jobs-and-apply-workflow)

View File

@ -0,0 +1,127 @@
---
id: orchestrator
title: Orchestrator
description: Job states, ready flow, and PDF generation/regeneration behavior.
sidebar_position: 1
---
## What it is
The Orchestrator is the primary jobs workspace in JobOps.
![Orchestrator jobs workspace](/img/features/orchestrator-jobs.png)
It controls:
- job lifecycle states
- manual and automatic ready flow
- PDF generation and regeneration
- handoff to post-application tracking
Job states:
- `discovered`: found by crawler/import, not tailored yet
- `processing`: tailoring and/or PDF generation in progress
- `ready`: tailored PDF generated and ready to apply
- `applied`: marked as applied
- `skipped`: explicitly excluded from active queue
- `expired`: deadline passed
## Why it exists
Orchestrator centralizes the transition from discovered opportunities to application-ready artifacts.
It exists to ensure:
- a consistent path from discovery to tailored output
- clear status transitions across manual and automated workflows
- predictable regeneration behavior when job data changes
## How to use it
### Intended ready flow
1. Manual flow:
1. Job starts in `discovered`.
2. Open the job and choose Tailor.
3. Edit JD/tailored fields/project picks.
4. Click **Finalize & Move to Ready**.
2. Auto flow:
1. Pipeline scores discovered jobs.
2. Top jobs above threshold are auto-processed.
3. Jobs move directly to `ready` with generated PDFs.
### Ghostwriter availability
Ghostwriter is available in `discovered` and `ready` job views.
For details, see [Ghostwriter](/docs/next/features/ghostwriter).
### Opening documentation from the sidebar
1. Open the sidebar menu.
2. In the footer section under `Version <build>`, click **Documentation**, which opens the locally hosted docs in a new tab.
### Generating PDFs
PDF generation uses:
- base resume selected from RxResume
- job description
- tailored summary/headline/skills/projects
Common paths:
- Discovered to finalization: `POST /api/jobs/actions` with `{ "action": "move_to_ready", "jobIds": ["<jobId>"] }`
- Ready regeneration: `POST /api/jobs/:id/generate-pdf`
### Regenerating PDFs after edits (copy-pasteable examples)
If JD or tailoring changes, regenerate PDF to keep output in sync.
```bash
curl -X PATCH "http://localhost:3001/api/jobs/<jobId>" \
-H "content-type: application/json" \
-d '{
"jobDescription": "<new JD>",
"tailoredSummary": "<optional>",
"tailoredHeadline": "<optional>",
"tailoredSkills": [{"name":"Backend","keywords":["TypeScript","Node.js"]}],
"selectedProjectIds": "p1,p2"
}'
```
```bash
curl -X POST "http://localhost:3001/api/jobs/<jobId>/summarize?force=true"
curl -X POST "http://localhost:3001/api/jobs/<jobId>/generate-pdf"
```
### External payload and sanitization defaults
- LLM prompts send minimized profile/job fields.
- Webhooks are sanitized and whitelisted by default.
- Logs and error details are redacted/truncated by default.
- Correlation fields include `requestId`, and when available `pipelineRunId` and `jobId`.
## Common problems
### Job is stuck in `processing`
- `processing` is transient; failures generally revert the job to `discovered`.
- Check run logs and retry generation.
### PDF does not reflect recent edits
- Run summarize with `force=true` after changing the JD/tailoring.
- Regenerate PDF after summarize completes.
### Reopen skipped/applied jobs
- Patch `status` back to `discovered` to return the job to the active queue.
## Related pages
- [Pipeline Run](/docs/next/features/pipeline-run)
- [Ghostwriter](/docs/next/features/ghostwriter)
- [Reactive Resume](/docs/next/features/reactive-resume)
- [Post-Application Tracking](/docs/next/features/post-application-tracking)

View File

@ -0,0 +1,100 @@
---
id: overview
title: Overview
description: Dashboard analytics for application volume, conversion, and response rate by source over selectable time windows.
sidebar_position: 1
---
## What it is
The Overview page is the analytics dashboard for your pipeline outcomes.
![Overview dashboard](/img/features/overview-dashboard.png)
It visualizes:
- Applications per day
- Application-to-response conversion
- Funnel progression (Applied, Screening, Interview, Offer, Rejected)
- Response rate by source
### Graph-level views
![Applications per day graph](/img/features/overview-applications-graph.png)
![Funnel progression graph](/img/features/overview-funnel-graph.png)
## Why it exists
The page helps you measure whether your current sourcing and tailoring approach is producing responses, not just applications.
Use it to quickly answer:
- Are application volumes increasing or dropping?
- Is response conversion improving?
- Where are applications stalling in the funnel?
- Which job boards are actually generating responses?
## How to use it
1. Open **Overview**.
2. Select a time window (`7d`, `14d`, `30d`, `90d`) in the top-right selector.
3. Review:
- **Applications per day** for volume trend
- **Application → Response Conversion** for quality/outcome trend
- **Response Rate by Source** to compare job board effectiveness
4. Compare periods and adjust your sourcing terms, filters, or tailoring strategy.
### Data and calculation defaults
- Default window is `30d`.
- Only jobs in statuses `applied` and `in_progress` are used as input.
- Conversion counts any positive response-stage event (for example recruiter screen, assessment, interview stages, or offer).
- Conversion trend chart uses a rolling window up to 7 days.
- Response rate by source is calculated across all time (not scoped to the duration selector), since response events may arrive well after the application window.
- Sources with fewer than 5 applications are hidden by default in the Response Rate by Source chart. Check **Include small samples** to show them.
### Response Rate by Source
The **Response Rate by Source** chart shows, for each job board (LinkedIn, Indeed, Gradcracker, etc.), what percentage of your applications received a non-rejection response.
**What counts as a response:** the application reached at least one of these stages — recruiter screen, assessment, hiring manager screen, technical interview, onsite, or offer. Ghosted applications (no stage events) and rejected outcomes are both excluded from the numerator.
Each bar is labelled `X% (n=Y)` where `n` is the number of applications from that source, so you can immediately tell whether a high rate comes from a meaningful sample or a single lucky application. The full breakdown (response rate, responded, applied) is also shown in the tooltip.
Sources are sorted by response rate descending. Sources with fewer than 5 applications are hidden by default to avoid misleading percentages from tiny samples. Check **Include small samples** to show them.
Use this chart to identify which sources produce genuine engagement versus silence, and concentrate future sourcing effort accordingly.
## Common problems
### Empty charts
- Verify you have jobs with `appliedAt` timestamps.
- The selected duration may exclude your recent activity.
### Conversion appears low
- Conversion only counts jobs that reached response stages.
- If stage events are missing or delayed, conversion will under-report.
### Trend icons look counterintuitive
- Volume trend compares first-half vs second-half averages in the selected window.
- Changing the time window can materially change trend direction.
### Response Rate by Source shows only one source
- Only sources with at least one applied job appear in the chart.
- If all your applications come from a single board, only that board will be shown.
### Response rate for a source looks too high or too low
- Check the `n=` value in the bar label or tooltip. A small sample (e.g. n=2) will produce an unreliable rate.
- Sources with fewer than 5 applications are hidden by default. If you see a suspiciously high rate, you may be looking at a small-sample source — check the n.
## Related pages
- [Orchestrator](/docs/next/features/orchestrator)
- [Post-Application Tracking](/docs/next/features/post-application-tracking)
- [Troubleshooting](/docs/next/troubleshooting/common-problems)

View File

@ -0,0 +1,118 @@
---
id: pipeline-run
title: Pipeline Run
description: How to use Run Mode (Automatic vs Manual), presets, source controls, and advanced run settings.
sidebar_position: 2
---
## What it is
Pipeline Run is the Jobs-page run modal for starting either:
- an **Automatic** pipeline run
- a **Manual** one-job import
For end-to-end sequence, read [Find Jobs and Apply Workflow](/docs/next/workflows/find-jobs-and-apply-workflow).
For manual import internals, read [Manual Import Extractor](/docs/next/extractors/manual).
## Why it exists
The modal provides one place to control run volume, source compatibility, and processing aggressiveness before consuming compute/time.
It helps you:
- choose speed vs depth with presets
- avoid invalid source/country combinations
- understand estimated run cost before starting
## How to use it
1. Open the Jobs page and use the top-right run control.
2. Choose either **Automatic** or **Manual** tab.
3. Configure required inputs and start run.
### Automatic tab
#### Presets
Three presets set defaults for run aggressiveness:
- **Fast**: lower processing volume, higher score threshold
- **Balanced**: middle-ground defaults
- **Detailed**: higher processing volume, lower score threshold
If values are edited manually, the UI shows **Custom**.
#### Country and source compatibility
- Country selection affects which sources are available.
- UK-only sources are disabled for non-UK countries.
- Adzuna is available only for its supported countries and when App ID/App Key are configured in Settings.
- Glassdoor can be enabled only when:
- selected country supports Glassdoor
- at least one **Search city** is set in Advanced settings
Incompatible sources are disabled with explanatory tooltips.
#### Advanced settings
- **Resumes tailored** (`topN`)
- **Min suitability score**
- **Max jobs discovered** (run budget cap)
- **Search cities** (optional multi-city input; required for Glassdoor)
#### Search terms
- Add terms with Enter or commas.
- Multiple terms increase discovery breadth and runtime.
- At least one search term is required.
#### Estimate and run gating
The footer estimate shows expected discovered jobs and resume-processing range.
`Start run now` is disabled when:
- a run is already in progress
- required save/run work is still in progress
- no compatible sources are selected
- no search terms are present
### Manual tab
Manual mode opens direct import flow in the same modal.
Use it when you already have a specific job description or link and do not want full discovery.
For accepted input formats, inference behavior, and limits, see [Manual Import Extractor](/docs/next/extractors/manual).
## Common problems
### Start button stays disabled
- Ensure at least one search term is present.
- Ensure at least one compatible source is selected.
- Wait for active save/run operations to finish.
### Glassdoor cannot be enabled
- Verify selected country supports Glassdoor.
- Set at least one Search city in Advanced settings.
### Adzuna is not selectable
- Set `Adzuna App ID` and `Adzuna App Key` in **Settings > Environment & Accounts**.
- Verify the selected country is one of Adzuna's supported markets.
### Run takes longer than expected
- Reduce term count.
- Use `Fast` preset or lower `Max jobs discovered`.
- Disable high-cost source combinations where acceptable.
## Related pages
- [Find Jobs and Apply Workflow](/docs/next/workflows/find-jobs-and-apply-workflow)
- [Manual Import Extractor](/docs/next/extractors/manual)
- [Orchestrator](/docs/next/features/orchestrator)
- [Overview](/docs/next/features/overview)

View File

@ -0,0 +1,93 @@
---
id: post-application-tracking
title: Post-Application Tracking
description: Gmail-based tracking inbox, smart routing, and review workflow.
sidebar_position: 3
---
The Tracking Inbox monitors Gmail for job-application responses and updates timelines.
![Tracking Inbox review queue](/img/features/tracking-inbox.png)
## Overview
1. Scans Gmail for recruitment-related emails
2. Matches emails to tracked jobs using AI
3. Updates timeline/state when confidence is high
4. Queues uncertain matches for manual review
## Smart router flow
```mermaid
flowchart TD
A[Recruitment email arrives in Gmail] --> B[Smart Router AI analyzes content]
B --> C{How confident is the match?}
C -->|95-100%| D[Auto-linked to job]
D --> E[Timeline updated automatically]
C -->|50-94%| F[Goes to Inbox for review with suggested match]
C -->|<50%| G{Is it relevant?}
G -->|Yes| H[Goes to Inbox as orphan]
G -->|No| I[Ignored]
```
## Setup
### Prerequisites
1. Gmail account with application emails
2. Google OAuth credentials
### Configure OAuth
Set:
```bash
GMAIL_OAUTH_CLIENT_ID=your-client-id.apps.googleusercontent.com
GMAIL_OAUTH_CLIENT_SECRET=your-client-secret
GMAIL_OAUTH_REDIRECT_URI=https://your-domain.com/oauth/gmail/callback
```
Then connect in UI via **Tracking Inbox → Connect Gmail**.
Detailed setup guide:
- [Gmail OAuth Setup](/docs/next/getting-started/gmail-oauth-setup)
## Using the inbox
- Review pending items in Tracking Inbox
- Approve to link/update timeline
- Ignore to mark non-relevant
Confidence interpretation:
- `95-100%`: auto-processed
- `50-94%`: pending review with suggestion
- `<50%`: pending review as orphan/ignored
## Privacy and security
- Scope requested: `gmail.readonly`
- Full scope: `https://www.googleapis.com/auth/gmail.readonly`
- Minimal metadata sent for matching
- Email data stays local in your instance
## API reference
| Method | Endpoint | Description |
| ------ | ----------------------------------------- | --------------------- |
| GET | `/api/post-application/inbox` | List pending messages |
| POST | `/api/post-application/inbox/:id/approve` | Approve message |
| POST | `/api/post-application/inbox/:id/deny` | Ignore message |
| GET | `/api/post-application/runs` | List sync runs |
| GET | `/api/post-application/providers/gmail/oauth/start` | Start OAuth flow |
| POST | `/api/post-application/providers/gmail/oauth/exchange` | Exchange OAuth code |
## Common issues
- No refresh token: disconnect and reconnect Gmail.
- Emails not appearing: check runs, OAuth config, and recruitment subjects.
- Wrong matches: expected in lower-confidence buckets; use manual review.

View File

@ -0,0 +1,260 @@
---
id: reactive-resume
title: Reactive Resume
description: Configure RxResume integration, base resume selection, and project inclusion behavior for PDF generation.
sidebar_position: 4
---
## What it is
Reactive Resume integration powers JobOps PDF generation.
JobOps uses a selected RxResume base resume as the source of truth, then applies job-specific tailoring (summary, headline, skills, project visibility) before exporting a PDF.
## Why it exists
Most users need a repeatable resume pipeline:
- one canonical resume source
- controlled project inclusion rules
- per-job tailored output without manual copy/paste
Reactive Resume integration provides that workflow end-to-end.
### Why JobOps uses RxResume (instead of building a new editor)
RxResume is a mature, established resume product with strong PDF output quality.
Key reasons:
- ATS-friendly PDF generation is already excellent and battle-tested.
- The editor UX is strong and supports extensive user customization.
- It supports many themes out of the box.
- It has a JSON-native model (import/export), which is critical for JobOps automation.
Because RxResume uses structured JSON, JobOps can safely apply LLM-driven updates to specific sections before generating PDFs.
## Core concepts
### Base resume
Your **base resume** is selected in Settings and used for:
- profile extraction
- project catalog extraction
- PDF generation
If no base resume is selected, profile-dependent features and PDF generation cannot run.
### Project catalog
JobOps reads projects from `sections.projects.items` in the selected RxResume resume.
Each project is identified by:
- `id`
- `name`
- `description`
- `date`
- `visible` (visible in base resume)
### Project selection controls
The Settings UI supports 3 controls:
- **Must Include**: always include these projects.
- **AI Selectable**: pool of projects AI can pick from.
- **Max Projects**: final cap for included projects.
At generation time:
1. Must-include projects are added first.
2. AI picks up to remaining slots from AI-selectable projects.
3. Final visible projects are applied to the generated resume.
## Setup and configuration
### Account requirements (important)
Before connecting Reactive Resume to JobOps:
1. Choose a mode in **Settings → Reactive Resume**:
- `v5` (API key)
- `v4` (email/password)
2. For **v5** (recommended for self-hosted/latest), generate an API key and configure `rxresumeApiKey` or `RXRESUME_API_KEY`.
3. For **v4**, create a native email/password account at [v4.rxresu.me/auth/register](https://v4.rxresu.me/auth/register) and configure `rxresumeEmail` + `rxresumePassword`.
Important:
- Explicit `v4` and `v5` modes do not silently fall back.
- OAuth-only logins are not supported for the v4 email/password integration.
### 1) Configure Reactive Resume access
Configure in **Settings → Reactive Resume**:
- `rxresumeMode` (`v5` or `v4`)
- `rxresumeApiKey` (for v5)
- `rxresumeEmail` + `rxresumePassword` (for v4)
Or via environment variables:
- `RXRESUME_MODE` (`v5` or `v4`)
- `RXRESUME_API_KEY` (for v5)
- `RXRESUME_EMAIL`
- `RXRESUME_PASSWORD`
- optional `RXRESUME_URL` (works for both modes; v5 OpenAPI path is added automatically)
### 2) Select base resume
In **Settings → Reactive Resume**:
1. Click refresh to fetch resumes.
2. Select the template/base resume.
3. Save settings.
### 3) Configure project behavior
In the same section:
1. Set `Max projects`.
2. Mark projects as **Must Include** where needed.
3. Mark remaining projects as **AI selectable**.
4. Save settings.
## Runtime behavior
### During PDF generation
High-level flow:
1. Load selected base resume from RxResume.
2. Apply tailored summary/headline/skills.
3. Compute final visible projects from your selection rules.
4. Optionally rewrite outbound links to tracer links (per-job toggle).
5. Create temporary resume in RxResume.
6. Export PDF.
7. Delete temporary resume.
### Per-job tracer links
Before generating a PDF, each job can enable/disable tracer links.
- Disabled: original RxResume links remain unchanged.
- Enabled: eligible outbound links are rewritten to `https://<your-host>/cv/<company>-xx` (readable slug + 2-letter suffix).
For background pipeline generation, configure:
- `JOBOPS_PUBLIC_BASE_URL=https://your-host`
Important:
- tracer enablement is gated by readiness checks
- if public host verification fails, enable is blocked until host health is restored
- toggle changes apply on next PDF generation only
### What JobOps changes with AI
Current AI-driven edits are intentionally scoped:
- `summary`
- `headline/title`
- `skills` and keywords
- project **visibility** (enable/disable per project)
## API reference
```bash
# Get effective settings (includes resolved resumeProjects and base resume id)
curl "http://localhost:3001/api/settings"
```
```bash
# Save base resume and project controls
curl -X PATCH "http://localhost:3001/api/settings" \
-H "content-type: application/json" \
-d '{
"rxresumeBaseResumeId": "resume_id_here",
"resumeProjects": {
"maxProjects": 4,
"lockedProjectIds": ["proj_a"],
"aiSelectableProjectIds": ["proj_b","proj_c","proj_d"]
}
}'
```
```bash
# List available Reactive Resume resumes
curl "http://localhost:3001/api/settings/rx-resumes"
```
```bash
# Fetch projects from one RxResume resume
curl "http://localhost:3001/api/settings/rx-resumes/<resumeId>/projects"
```
```bash
# Regenerate PDF for a job after changing settings or resume data
curl -X POST "http://localhost:3001/api/jobs/<jobId>/generate-pdf"
```
## Troubleshooting and FAQ
### RxResume controls are disabled
- Ensure the selected mode has credentials configured.
- `v5`: set a valid API key.
- `v4`: set email + password.
- Save settings, then refresh resumes in the Reactive Resume section.
### No resumes appear in dropdown
- Confirm the selected mode matches your Reactive Resume deployment.
- For `v5`, confirm `RXRESUME_API_KEY` / `rxresumeApiKey` is valid for your self-hosted instance.
- For `v4`, confirm credentials are valid for [v4.rxresu.me](https://v4.rxresu.me) (or your configured v4 URL) and are not OAuth-only.
- Confirm the selected Reactive Resume account actually has resumes.
### Project list is empty in settings
- Root cause is usually the source resume on [rxresu.me](https://rxresu.me) having an empty **Projects** section.
- Add projects directly in RxResume first.
- Re-select/refresh the base resume in JobOps and regenerate the PDF.
### Project checkboxes look wrong after changing base resume
- Save after selecting the new base resume.
- Re-open Reactive Resume section and verify project IDs from that resume.
- Re-run PDF generation to apply the new project map.
### Changes did not affect an already generated PDF
- Settings changes apply to new generation runs.
- Regenerate PDFs for already-ready jobs.
## Best practices
- Keep base resume projects complete and up to date in RxResume.
- Use **Must Include** sparingly for cornerstone projects.
- Keep AI-selectable pool broad enough for job-specific relevance.
- After major resume edits, regenerate PDFs for active high-priority jobs.
### Add “context projects” even if they are usually hidden
The LLM only knows what exists in your resume data.
That means there is real value in adding additional projects in RxResume, even if you keep them hidden by default:
- They increase the AIs context about your skills and range.
- They can be toggled on only when relevant to a role.
Example:
- If your main background is not Android, but you have one credible Android side project, include it in RxResume, but keep it hidden by default.
- For a mobile role, the AI can enable that project automatically based on the job description.
## Related pages
- [Settings](./settings)
- [Orchestrator](./orchestrator)
- [Ghostwriter](./ghostwriter)
- [Self-Hosting](../getting-started/self-hosting)

View File

@ -0,0 +1,194 @@
---
id: settings
title: Settings
description: Configure models, webhooks, accounts, backup behavior, scoring, and safety controls.
sidebar_position: 2
---
## What it is
The Settings page is the control center for app-wide behavior.
![Settings page sections](/img/features/settings.png)
It lets you configure:
- LLM provider and models
- Webhook destinations and secret
- Display and Ghostwriter defaults
- Service credentials and basic auth
- Reactive Resume project selection
- Tracer Links readiness verification
- Backup and scoring rules
- Data-clearing actions in the Danger Zone
## Why it exists
Most teams want stable defaults for repeated workflows, without editing environment variables every time.
Settings gives you runtime overrides for the key parts of discovery, scoring, tailoring, and post-application automation.
## How to use it
1. Open **Settings**.
2. Expand each section you want to change.
3. Update values and click **Save Changes**.
4. Re-run the workflow that uses those settings (for example pipeline runs, Ghostwriter, or resume tailoring) to verify behavior.
## Section-by-section guide
### Model
![Model settings section](/img/features/settings-model-section.png)
- Choose provider (`openrouter`, `lmstudio`, `ollama`, `openai`, `gemini`)
- Set provider-specific base URL/API key when required
- Configure default model plus task-specific overrides:
- Scoring model
- Tailoring model
- Project-selection model
### Webhooks
![Webhooks settings section](/img/features/settings-webhooks-section.png)
- Pipeline status webhook: called on run completion/failure
- Job completion webhook: called when a job is marked applied
- Optional webhook secret (sent as bearer token)
### Display Settings
![Display settings section](/img/features/settings-display-section.png)
- Toggle visa sponsor badge visibility in job lists/details
### Ghostwriter
![Ghostwriter settings section](/img/features/settings-ghostwriter-section.png)
- Set global writing defaults:
- Tone
- Formality
- Constraints
- Do-not-use terms
### Reactive Resume
![Reactive Resume settings section](/img/features/settings-reactive-resume-section.png)
- Select a template/base resume
- Configure project selection behavior:
- Max projects
- Must-include projects
- AI-selectable projects
### Tracer Links
- Verify tracer readiness before enabling per-job tracing
- Shows current status (`Ready`, `Unavailable`, `Unconfigured`, or stale state)
- Displays the effective public base URL and last check time
- Provides **Verify now** for an on-demand health check
Readiness requires:
- a valid public JobOps base URL
- successful reachability of `<public-base-url>/health`
- non-localhost/non-private host setup for public redirect usage
### Environment & Accounts
- Configure service accounts:
- RxResume email/password
- UKVisaJobs email/password
- Adzuna app ID/app key
- Optional basic authentication for write operations
### Backup
![Backup settings section](/img/features/settings-backup-section.png)
- Enable/disable automatic daily backups
- Configure backup hour (UTC) and max retained backups
- Create or delete backups manually
- See [Database Backups](../getting-started/database-backups) for full backup/restore guidance.
### Scoring Settings
![Scoring settings section](/img/features/settings-scoring-section.png)
- Penalize missing salary data
- Set penalty amount
- Optional auto-skip threshold for low-score jobs
- Block jobs from companies that match configured keyword tokens
### Danger Zone
![Danger Zone settings section](/img/features/settings-danger-zone-section.png)
- Clear jobs by selected statuses
- Clear jobs below a score threshold
- Clear the full database
## API examples
```bash
# Get effective settings (defaults + overrides)
curl "http://localhost:3001/api/settings"
```
```bash
# Update settings overrides
curl -X PATCH "http://localhost:3001/api/settings" \
-H "content-type: application/json" \
-d '{
"llmProvider": "openrouter",
"model": "openai/gpt-4.1-mini",
"chatStyleTone": "concise",
"showSponsorInfo": true
}'
```
```bash
# List and create backups (used by the Backup section)
curl "http://localhost:3001/api/backups"
curl -X POST "http://localhost:3001/api/backups"
```
## Common problems
### Saved value does not seem to apply
- Some settings apply only to new runs/actions after save.
- Re-run scoring/tailoring/pipeline to validate effect.
### RxResume controls are disabled
- Configure RxResume credentials in Environment & Accounts first.
- Then refresh available resumes from the Reactive Resume section.
### RxResume projects look empty in the RxResume UI
- Root cause: your resume on [rxresu.me](https://rxresu.me) has an empty **Projects** section.
- Fix in RxResume first: add project entries to the base resume you selected in Settings.
- Then return to JobOps, refresh/select the same base resume in **Reactive Resume**, and regenerate the PDF.
- JobOps preserves current visibility state, but it cannot create missing project content if the source resume has no projects.
### Webhook calls fail
- Verify URL reachability from the server host.
- Confirm auth expectations on the receiver side (including secret/bearer token).
### Tracer links cannot be enabled
- Open **Settings → Tracer Links** and click **Verify now**.
- Ensure `JOBOPS_PUBLIC_BASE_URL` is set for background/pipeline usage.
- Ensure the configured host is publicly reachable and `/health` responds.
## Related pages
- [Reactive Resume](./reactive-resume)
- [Database Backups](../getting-started/database-backups)
- [Overview](./overview)
- [Orchestrator](./orchestrator)
- [Ghostwriter](./ghostwriter)
- [Self-Hosting](../getting-started/self-hosting)

View File

@ -0,0 +1,151 @@
---
id: tracer-links
title: Tracer Links
description: Track outbound resume-link clicks with per-job toggles and privacy-safe analytics.
sidebar_position: 8
---
## What it is
Tracer Links are per-job redirect links that are generated when a PDF is created.
When enabled for a job, JobOps rewrites eligible outbound RxResume links to your JobOps host, then redirects to the original destination after recording a click event.
Examples:
- original: `https://github.com/yourname`
- traced: `https://jobops.dakheera47.com/cv/amazon-de`
Format details:
- path prefix is always `/cv/`
- token format is `<company-slug>-<xx>`
- `<xx>` is two lowercase letters (`a-z`)
- visible link text in the PDF is also updated to the traced URL
## Why it exists
Without tracer links, resume links are "fire and forget".
Tracer links let you answer:
- whether links in a specific job PDF were opened
- which destination links are being opened most
- rough human vs bot traffic split
- per-job and global engagement trends over time
The feature is privacy-safe by design:
- no raw IP is stored
- referrer host is stored (not full referrer URL)
- bot traffic is flagged and can be filtered in analytics
## How to use it
1. Open **Settings** and go to the **Tracer Links** section.
2. Click **Verify now** and confirm status is **Ready**.
3. Open a job in **Jobs**.
4. Enable **Tracer links for this job** in tailoring or job details.
5. Generate or regenerate the PDF.
6. Open **Tracer Links** in navigation to view:
- global totals
- top jobs and top links
- per-job drilldown by Job ID
Important behavior:
- Tracer links are **off by default** per job.
- Toggle changes apply on the **next PDF generation only**.
- Existing PDFs are not modified retroactively.
- Existing tracer URLs remain valid, even if a newer PDF generates new links.
### Readiness and enable/disable behavior
You can only turn tracer links **on** when readiness is healthy.
Readiness checks:
- a resolvable public base URL
- a successful health probe to `<public-base-url>/health`
- a non-localhost/non-private host for public usage
If readiness is unavailable, enable is blocked until verification passes.
### Required background-run setting
If PDFs are generated by background pipeline runs, set:
```bash
JOBOPS_PUBLIC_BASE_URL=https://your-jobops-host
```
JobOps uses this URL when request host inference is not available.
### URL uniqueness rules
Tracer links are unique enough for tracking while still readable.
- same job + same source path + same destination URL => token is reused
- same job + same source path + changed destination URL => new token
- old tokens continue to redirect (not retroactively deleted)
### Risk and responsibility disclaimer
Tracer links are redirect links. Some recruiters, companies, universities, or security tools may treat redirects as suspicious behavior and may whitelist, blacklist, filter, or flag these links as phishing-like.
By enabling and using this feature, you accept full responsibility for any consequences that result from its use. Responsibility for policy, trust, and reputation outcomes sits with the user/operator of the instance, not with the app.
## Common problems
### I cannot enable tracer links
Cause:
- readiness is not **Ready**
- host is local/private or unreachable from the verifier
Fix:
- configure a real public host
- set `JOBOPS_PUBLIC_BASE_URL` for background flows
- make sure `<public-base-url>/health` is reachable
- retry **Verify now**
### Tracer links enabled but PDF generation fails
Cause:
- base URL cannot be resolved at generation time, or instance health is not reachable for that run
Fix:
- ensure `JOBOPS_PUBLIC_BASE_URL` is set correctly
- verify the deployment is publicly reachable
- regenerate the PDF
### I enabled tracer links, but old PDF still has direct links
Cause:
- toggle changes only apply to newly generated PDFs
Fix:
- regenerate the PDF for that job
### Analytics look inflated by scanners
Cause:
- link scanners and preview bots may open links automatically
Fix:
- use the **Include likely bots** filter in Tracer Links analytics
## Related pages
- [Settings](/docs/next/features/settings)
- [Reactive Resume](/docs/next/features/reactive-resume)
- [Find Jobs and Apply Workflow](/docs/next/workflows/find-jobs-and-apply-workflow)
- [Post-Application Tracking](/docs/next/features/post-application-tracking)

View File

@ -0,0 +1,74 @@
---
id: visa-sponsors
title: Visa Sponsors
description: Search the UK licensed sponsor register and use sponsor matches in your job workflow.
sidebar_position: 4
---
## What it is
The Visa Sponsors page lets you search the UK Home Office licensed sponsor register from inside JobOps.
For each company, it shows:
- Match score against your query
- Company location (when available)
- Licensed routes and type/rating details
- Last data refresh time and sponsor count
## Why it exists
Many roles require sponsorship-ready employers. This page helps you quickly validate whether a target company appears on the official sponsor list, so you can prioritize applications and sourcing terms.
## How to use it
1. Open **Visa Sponsors** in the app.
2. Enter a company name in the search box.
3. Select a result to view sponsor details.
4. Use the score and route details to decide whether to prioritize that employer.
### Refresh schedule
- Automatic update runs daily at about **02:00** (server local time).
- Use the download/update button in the page header to fetch the latest register immediately.
### API examples
```bash
# Search sponsors
curl -X POST http://localhost:3001/api/visa-sponsors/search \
-H "content-type: application/json" \
-d '{"query":"Monzo","limit":100,"minScore":20}'
```
```bash
# Get one organization's entries (all licensed routes)
curl "http://localhost:3001/api/visa-sponsors/organization/Monzo%20Bank%20Ltd"
```
```bash
# Trigger manual refresh
curl -X POST http://localhost:3001/api/visa-sponsors/update
```
## Common problems
### No results found
- Try alternate legal names (`Ltd`, `Limited`, abbreviations).
- Reduce spelling strictness by searching a shorter core name.
### Sponsor data is empty
- Run a manual refresh with the header update button (or `POST /api/visa-sponsors/update`).
- Check that the server can reach `gov.uk` and `assets.publishing.service.gov.uk`.
### Company appears once but has multiple routes
- Open the detail panel for that company; route/type entries are shown there.
## Related pages
- [Orchestrator](/docs/next/features/orchestrator)
- [Post-Application Tracking](/docs/next/features/post-application-tracking)
- [Self-Hosting](/docs/next/getting-started/self-hosting)

View File

@ -0,0 +1,127 @@
---
id: database-backups
title: Database Backups
description: Configure, run, and restore JobOps database backups.
sidebar_position: 2
---
## What this covers
This page is about database backups:
- automatic backup schedule
- manual backup creation/deletion
- retention behavior
- restore workflow
- backup troubleshooting
## Backup behavior
JobOps stores backups in the same data directory as `jobs.db`.
Two backup types exist:
- **Automatic** backups
- **Manual** backups
### Automatic backups
- Scheduled daily.
- Filename format: `jobs_YYYY_MM_DD.db`
- Schedule hour is configured in Settings (**UTC hour**).
- Automatic retention is capped by `backupMaxCount`.
- If todays automatic backup already exists, JobOps skips creating a duplicate.
### Manual backups
- Triggered from Settings or `POST /api/backups`.
- Filename format: `jobs_manual_YYYY_MM_DD_HH_MM_SS.db`
- If a filename collision occurs, JobOps appends `_1`, `_2`, etc.
- Manual backups are **not** auto-deleted by automatic retention cleanup.
## Configure backups
In **Settings → Backup**:
1. Enable automatic backups.
2. Set backup hour (`0-23`, UTC).
3. Set max automatic backups to keep (`1-5`).
4. Save settings.
## API reference
```bash
# List backups + next scheduled run time
curl "http://localhost:3001/api/backups"
```
```bash
# Create a manual backup
curl -X POST "http://localhost:3001/api/backups"
```
```bash
# Delete a specific backup
curl -X DELETE "http://localhost:3001/api/backups/jobs_manual_2026_02_15_10_20_30.db"
```
```bash
# Update backup settings via Settings API
curl -X PATCH "http://localhost:3001/api/settings" \
-H "content-type: application/json" \
-d '{
"backupEnabled": true,
"backupHour": 2,
"backupMaxCount": 5
}'
```
## Restore workflow
To restore from a backup:
1. Stop JobOps.
2. Locate backup files in your data directory.
3. Copy the chosen backup over the main DB file (`jobs.db`).
4. Start JobOps.
5. Verify jobs/runs in the UI.
Example shell flow:
```bash
# Example only: adjust paths for your setup
cp /path/to/data/jobs_manual_2026_02_15_10_20_30.db /path/to/data/jobs.db
```
## Troubleshooting
### Backups are not running automatically
- Confirm `backupEnabled` is true.
- Confirm backup hour is set as intended (UTC, not local time).
- Verify the app process is running at scheduled time.
### `POST /api/backups` fails
- Confirm the data directory and `jobs.db` are writable/readable.
- Confirm `jobs.db` exists.
- In demo mode, manual backup creation is blocked.
### Cannot delete a backup
- Filename must match valid backup patterns.
- Invalid names and missing files return errors.
### Next scheduled time is null
- Automatic backups are currently disabled.
## Notes
- Backup cleanup applies only to automatic backups.
- Manual backups stay until you delete them.
## Related pages
- [Settings](../features/settings)
- [Self-Hosting](./self-hosting)

View File

@ -0,0 +1,96 @@
---
id: gmail-oauth-setup
title: Gmail OAuth Setup
description: Step-by-step Google Cloud setup for JobOps Gmail tracking, with exact scopes and callback configuration.
sidebar_position: 2
---
## What it is
This guide configures Google OAuth so JobOps can read recruitment emails from Gmail for the Tracking Inbox.
## Why it exists
Gmail OAuth setup is easy to misconfigure (wrong redirect URI, missing refresh token, or unnecessary scopes). This page documents the exact defaults JobOps expects.
## How to use it
### 1) Create Google Cloud credentials
In [Google Cloud Console](https://console.cloud.google.com/):
1. Create (or select) a project.
2. Open **APIs & Services → Library** and enable **Gmail API**.
3. Open **APIs & Services → OAuth consent screen** and configure your app.
4. Open **APIs & Services → Credentials** and create **OAuth client ID**.
5. Choose **Web application**.
6. Add at least one authorized redirect URI:
- Local: `http://localhost:3005/oauth/gmail/callback`
- Production: `https://your-domain.com/oauth/gmail/callback`
Notes:
- If you set `GMAIL_OAUTH_REDIRECT_URI`, it must exactly match a redirect URI in Google Cloud.
- JobOps does not require JavaScript origins for this flow.
### 2) Set environment variables
Configure:
```bash
GMAIL_OAUTH_CLIENT_ID=your-client-id.apps.googleusercontent.com
GMAIL_OAUTH_CLIENT_SECRET=your-client-secret
# Optional (recommended in production)
GMAIL_OAUTH_REDIRECT_URI=https://your-domain.com/oauth/gmail/callback
```
Then restart the container/app.
### 3) Connect Gmail in JobOps
1. Open **Tracking Inbox**.
2. Click **Connect Gmail**.
3. Complete Google consent.
JobOps starts OAuth with:
- Scope: `https://www.googleapis.com/auth/gmail.readonly`
- `access_type=offline` (requests refresh token)
- `prompt=consent` (forces consent screen so refresh token is returned reliably)
### 4) Scope reference (required vs not required)
Required by JobOps:
- `https://www.googleapis.com/auth/gmail.readonly`
Not required for JobOps Gmail ingestion:
- `https://www.googleapis.com/auth/gmail.modify`
- `openid`
- `https://www.googleapis.com/auth/userinfo.email`
- `https://www.googleapis.com/auth/userinfo.profile`
## Common problems
### Redirect URI mismatch
- Symptom: Google returns `redirect_uri_mismatch`.
- Fix: ensure the exact callback URL in `GMAIL_OAUTH_REDIRECT_URI` is also present in the OAuth client redirect URIs.
### No refresh token returned
- Symptom: connect fails after OAuth exchange.
- Fix: remove app access in your Google account, then reconnect so consent is re-granted.
### Gmail connects but no inbox results
- Check that your account actually has recruitment/application emails.
- Trigger a sync and increase `searchDays` if needed.
## Related pages
- [Self-Hosting (Docker Compose)](/docs/next/getting-started/self-hosting)
- [Post-Application Tracking](/docs/next/features/post-application-tracking)
- [Post-Application Workflow](/docs/next/workflows/post-application-workflow)
- [Common Problems](/docs/next/troubleshooting/common-problems)

View File

@ -0,0 +1,130 @@
---
id: self-hosting
title: Self-Hosting (Docker Compose)
description: Deploy JobOps with Docker Compose and configure onboarding integrations.
sidebar_position: 1
---
The easiest way to run JobOps is via Docker Compose. The app is self-configuring and guides you through setup on first launch.
## Prerequisites
- Docker Desktop or Docker Engine + Compose v2
## 1) Start the stack
No environment variables are required to boot:
```bash
docker compose up -d
```
This pulls the pre-built image from GHCR and starts the API, UI, and scrapers in one container.
To build locally instead:
```bash
docker compose up -d --build
```
## 2) Access the app and onboard
Open:
- **Dashboard**: `http://localhost:3005`
The onboarding wizard helps you validate and save:
1. **LLM Provider**: OpenRouter by default (or OpenAI/Gemini/local URL).
2. **PDF Export**: RxResume credentials for PDF generation.
3. **Template Resume**: Choose a base resume from your RxResume account.
Settings are saved to the local database.
## Gmail OAuth (Tracking Inbox)
If you want Gmail integration, configure OAuth credentials.
### 1) Create Google OAuth credentials
In Google Cloud:
1. Configure OAuth consent screen.
2. Enable Gmail API.
3. Create OAuth client ID (`Web application`).
4. Add redirect URI:
- `http://localhost:3005/oauth/gmail/callback`
- Or your production URL, for example `https://your-domain.com/oauth/gmail/callback`
### 2) Configure environment variables
- `GMAIL_OAUTH_CLIENT_ID` (required)
- `GMAIL_OAUTH_CLIENT_SECRET` (required)
- `GMAIL_OAUTH_REDIRECT_URI` (optional, recommended in production)
### 3) Restart and connect
- Restart container
- Open Tracking Inbox and click **Connect Gmail**
For a full step-by-step setup, exact scope requirements, and troubleshooting, see:
- [Gmail OAuth Setup](/docs/next/getting-started/gmail-oauth-setup)
## Email-to-job matching overview
```mermaid
flowchart TD
A[Recruitment email arrives in Gmail] --> B[Smart Router AI analyzes content]
B --> C{How confident is the match?}
C -->|95-100%| D[Auto-linked to job]
D --> E[Timeline updated automatically]
C -->|50-94%| F[Goes to Inbox for review with suggested job match]
C -->|<50%| G{Is it relevant?}
G -->|Yes| H[Goes to Inbox as orphan]
G -->|No| I[Ignored]
F --> J{User review}
H --> J
J -->|Approve| K[Linked to job + timeline update]
J -->|Ignore| L[Marked not relevant]
```
## Persistent data
`./data` bind-mount stores:
- SQLite DB: `data/jobs.db`
- Generated PDFs: `data/pdfs/`
## Public demo mode
Set `DEMO_MODE=true` for sandbox deployments.
Behavior in demo mode:
- Works locally: browsing/filtering/status/timeline edits
- Simulated: pipeline run/summarize/process/rescore/pdf/apply
- Blocked: settings writes, DB clear, backups
- Auto-reset: every 6 hours
## Updating
```bash
git pull
docker compose pull
docker compose up -d
```
## Self-hosted Reactive Resume
If you self-host Reactive Resume, set:
- `RXRESUME_URL=http://rxresume.local.net`
- `RXRESUME_MODE=auto` (recommended) or `v5`/`v4` to force a specific API version
- `RXRESUME_API_KEY=...` (or configure `rxresumeApiKey` in JobOps Settings)
`auto` mode is the default and prefers v5 when an API key is configured, then falls back to v4 credentials.

View File

@ -0,0 +1,139 @@
---
id: intro
title: JobOps Documentation
description: Documentation index for setup, features, extractors, and common problems.
sidebar_position: 1
slug: /
---
Welcome to the JobOps documentation. This site contains guides for setup, configuration, and day-to-day usage.
## Getting Started
- **[Self-Hosting Guide](/docs/next/getting-started/self-hosting)**
- Docker setup instructions
- Gmail OAuth configuration for email tracking
- Environment variables reference
- Demo mode deployment
- **[Database Backups](/docs/next/getting-started/database-backups)**
- Automatic backup scheduling and retention
- Manual backup creation/deletion
- Restore workflow and troubleshooting
## Workflows
- **[Find Jobs and Apply Workflow](/docs/next/workflows/find-jobs-and-apply-workflow)**
- Run pipeline first, then review discovered and ready jobs
- Use fit assessment and score to prioritize applications
- Mark jobs as applied to trigger webhooks and analytics
- **[Post-Application Workflow](/docs/next/workflows/post-application-workflow)**
- Track events manually for direct control
- Or configure automatic Gmail sync and inbox review
- Move confirmed updates into in-progress tracking
## Feature Documentation
- **[Orchestrator](/docs/next/features/orchestrator)**
- Job states explained (`discovered`, `ready`, `applied`, etc.)
- The ready flow (manual vs auto)
- PDF generation and regeneration
- Post-application tracking overview
- **[Pipeline Run](/docs/next/features/pipeline-run)**
- Run modal controls (`Automatic` vs `Manual`)
- Presets, source/country compatibility, and advanced settings
- Run estimate and start conditions
- **[Job Search Bar](/docs/next/features/job-search-bar)**
- Open with `Cmd+K` / `Ctrl+K` or the Search button
- Fuzzy search across title, company, and location
- Use `@status` lock syntax to scope results quickly
- **[Keyboard Shortcuts](/docs/next/features/keyboard-shortcuts)**
- Full Jobs-page shortcut reference by context
- `?` shortcut help dialog and `Control` hint bar behavior
- Tab-specific actions like skip, move to ready, and mark applied
- **[Multi-Select and Bulk Actions](/docs/next/features/multi-select-and-bulk-actions)**
- Select many jobs using row checkboxes or select-all
- Run bulk move, skip, and rescore actions from the floating action bar
- Keyboard support for select, clear, and fast bulk move-to-ready
- **[Settings](/docs/next/features/settings)**
- LLM provider/model and task-specific overrides
- Webhooks, service accounts, and basic auth controls
- Backup scheduling, scoring thresholds, and danger-zone cleanup tools
- **[Reactive Resume](/docs/next/features/reactive-resume)**
- Base resume selection and RxResume integration
- Project inclusion controls (must-include, AI-selectable, max)
- PDF generation behavior and troubleshooting
- **[Applications Overview](/docs/next/features/overview)**
- Applications-per-day trend
- Conversion analytics and funnel
- Duration window controls (`7d`, `14d`, `30d`, `90d`)
- **[In Progress Board](/docs/next/features/in-progress-board)**
- Pre-application vs post-application workflow split
- Kanban tracking for higher-attention opportunities
- Drag-and-drop stage management
- **[Ghostwriter](/docs/next/features/ghostwriter)**
- One persistent conversation per job
- Streaming responses, stop, and regenerate
- Markdown rendering and drawer behavior
- Writing style settings impact
- **[Post-Application Tracking](/docs/next/features/post-application-tracking)**
- How the Smart Router AI works
- Gmail integration setup
- Using the Tracking Inbox
- Privacy and security details
- API reference
- **[Visa Sponsors](/docs/next/features/visa-sponsors)**
- Search licensed UK sponsor organizations
- Review company routes and sponsor ratings
- Trigger manual data refresh
## Extractors
- **[Extractors Overview](/docs/next/extractors/overview)**
- **[Gradcracker](/docs/next/extractors/gradcracker)**
- **[UKVisaJobs](/docs/next/extractors/ukvisajobs)**
- **[JobSpy](/docs/next/extractors/jobspy)**
- **[Manual Import](/docs/next/extractors/manual)**
## Quick Reference
### Main Components
- **Orchestrator**: Main application (UI, API, database)
- **Extractors**: Specialized job crawlers
- **Shared**: Common types and utilities
### Key Features
1. **Job Discovery**: Automatically find jobs from multiple sources.
2. **AI Scoring**: Rank jobs by suitability for your profile.
3. **Resume Tailoring**: Generate custom resumes for each job.
4. **PDF Export**: Create tailored PDFs via RxResume integration.
5. **Application Tracking**: Monitor your applied jobs.
6. **Email Tracking**: Auto-track post-application responses.
## Contributing to Documentation
When adding user-visible behavior:
1. Update the relevant feature page in current docs.
2. Add API documentation where relevant.
3. Keep examples realistic and copy-pasteable.
4. Include diagrams for non-trivial workflows.
## Support
- Open an [issue](https://github.com/DaKheera47/job-ops/issues) for documentation errors.
- Check these docs before opening support requests.

View File

@ -0,0 +1,36 @@
---
id: documentation-style-guide
title: Documentation Style Guide
description: Standards for writing user-facing docs in this repository.
sidebar_position: 2
---
Use this structure for feature pages:
1. **What it is**
2. **Why it exists**
3. **How to use it**
4. **Common problems**
5. **Related pages**
## Frontmatter requirements
Every doc should include:
- `id`
- `title`
- `description`
- `sidebar_position`
## Writing rules
- Prefer concrete steps over abstract prose.
- Provide copy-pasteable examples.
- State defaults and constraints explicitly.
- Use absolute `/docs/...` URLs and include the version segment when needed.
- For current docs, use `/docs/next/...` links.
- For versioned docs, link within that version (for example `/docs/...` in `version-0.1.20`).
## PR expectations
Any user-visible behavior change should include matching docs updates.

View File

@ -0,0 +1,30 @@
---
id: faq
title: FAQ
description: Frequently asked questions about deployment, docs, and operations.
sidebar_position: 1
---
## Is docs content bundled for self-hosted installs?
Yes. The docs static build is bundled and served locally at `/docs`.
## How are docs versions managed?
Docs are versioned using Docusaurus versions, intended to map to release tags.
## Where should contributors edit docs?
Edit files under `docs-site/docs` for latest docs.
## What does this cost in practice?
Real-world reference: from early December 2025 to mid-February 2026, with heavy usage and testing (about 10 to 15 applications per day), easily more than 3000 jobs scored, total LLM spend was about **$12 USD** using Gemini 3 Flash through OpenRouter.
Cost varies by:
- selected model/provider
- prompt volume and size
- number of jobs scored/tailored per run
For this workload, Gemini 3 Flash has been low-cost while still producing high-quality outputs.

View File

@ -0,0 +1,41 @@
---
id: common-problems
title: Common Problems
description: Quick fixes for the most frequent setup and runtime issues.
sidebar_position: 1
---
## Docs site not loading at `/docs`
- Confirm docs build exists:
```bash
npm --workspace docs-site run build
```
- In production, ensure container includes docs build artifact.
## Deep links under `/docs/*` return 404
- Confirm Express is serving docs static mount before app SPA fallback.
- Confirm docs base URL is `/docs/` in `docs-site/docusaurus.config.ts`.
## Gmail OAuth callback fails
- Verify `GMAIL_OAUTH_CLIENT_ID`, `GMAIL_OAUTH_CLIENT_SECRET`.
- Ensure authorized redirect URI exactly matches deployment callback URL.
## No job scoring or AI inference
- Validate `LLM_API_KEY` and provider settings.
- Check settings page and API connectivity.
## PDF generation fails
- Verify RxResume credentials.
- Confirm selected base resume exists and is accessible.
## UKVisaJobs runs fail
- Re-authenticate by removing cached auth file or forcing refresh.
- Verify extractor credentials and API response behavior.

View File

@ -0,0 +1,95 @@
---
id: add-an-extractor
title: Add an Extractor
description: How to add a new extractor using the manifest contract and shared extractor catalog.
sidebar_position: 2
---
## What it is
This guide explains how to add a new extractor that is auto-registered at orchestrator startup.
The extractor runtime is discovered from a local `manifest.ts` file, and the source is type-safe across API/client through the shared catalog in `shared/src/extractors/index.ts`.
Extractor manifests must live in extractor packages under `extractors/<name>/` only. Do not add manifest files inside `orchestrator/`.
Extractor run logic should also live in the extractor package so orchestrator stays extractor-agnostic.
## Why it exists
Without a manifest contract, adding extractors required touching multiple orchestrator files.
With the manifest system, contributors only need to:
1. Add a manifest in their extractor package.
2. Add the new source id to the shared typed catalog.
That keeps runtime wiring dynamic while preserving compile-time safety in API and client code.
## How to use it
1. Create your extractor package under `extractors/<name>/`.
2. Add a `manifest.ts` in the extractor package root (or `src/manifest.ts`).
- Valid locations are only `extractors/<name>/manifest.ts` or `extractors/<name>/src/manifest.ts`.
- `orchestrator/**/manifest.ts` is not used for extractor discovery.
3. Export a manifest with:
- `id`
- `displayName`
- `providesSources`
- `requiredEnvVars` (optional)
- `run(context)` that returns `{ success, jobs, error? }`
4. Add the new source id to `shared/src/extractors/index.ts`:
- append to `EXTRACTOR_SOURCE_IDS`
- add an entry in `EXTRACTOR_SOURCE_METADATA`
5. Ensure your extractor maps output to `CreateJobInput[]`.
6. Run the full CI checks.
Example manifest:
```ts
import type { ExtractorManifest } from "@shared/types/extractors";
export const manifest: ExtractorManifest = {
id: "myextractor",
displayName: "My Extractor",
providesSources: ["myextractor"],
requiredEnvVars: ["MYEXTRACTOR_API_KEY"],
async run(context) {
// context.searchTerms, context.settings, context.onProgress, context.shouldCancel
const jobs = [];
return { success: true, jobs };
},
};
export default manifest;
```
Subprocess extractors are supported. Keep subprocess spawning inside `run(context)` so orchestrator only depends on the manifest contract.
## Common problems
### Extractor not discovered at startup
- Check file path: `extractors/<name>/manifest.ts` or `extractors/<name>/src/manifest.ts`.
- Ensure the file exports `default` or named `manifest`.
### Source compiles in extractor but fails in API/client
- Add the new source id to `shared/src/extractors/index.ts`.
- Confirm metadata exists for that source id.
### Source appears in shared catalog but is unavailable at runtime
- The manifest was not loaded successfully.
- Check startup logs for registry warnings.
### Source requires credentials but never returns jobs
- Add and validate `requiredEnvVars`.
- Verify your manifest `run(context)` reads settings/env values correctly.
## Related pages
- [Extractors Overview](/docs/next/extractors/overview)
- [Adzuna Extractor](/docs/next/extractors/adzuna)
- [Hiring Cafe Extractor](/docs/next/extractors/hiring-cafe)
- [UKVisaJobs Extractor](/docs/next/extractors/ukvisajobs)

View File

@ -0,0 +1,99 @@
---
id: find-jobs-and-apply-workflow
title: Find Jobs and Apply Workflow
description: Recommended end-to-end pre-application workflow from pipeline run to marking jobs as applied.
sidebar_position: 1
---
## Goal
This guide documents the main intended pre-application workflow in JobOps.
If you follow this order, you get the strongest results from discovery, scoring, tailoring, and tracking.
## Recommended flow (in order)
### 1) Run a pipeline first
From the **Jobs** page, use the top-right pipeline/run control.
What this does:
- fetches jobs from enabled extractors
- scores relevance against your resume/profile
- optionally tailors top jobs and prepares PDFs
Important:
- Some scrapers are slower and can take significant time.
- Larger scrape ranges and more sources increase run duration.
### 2) Configure pipeline advanced settings
In pipeline advanced settings, configure:
- how many jobs to discover (approximate target)
- minimum score threshold for tailoring
- how many jobs should be tailored/generated
This directly controls how many jobs appear downstream in `discovered` and `ready`.
### 3) Review the `Discovered` column
After the run, `discovered` is populated with jobs found by extractors.
For each discovered job:
- review the suitability score
- read the AI fit justification in **Fit Assessment**
- decide whether the opportunity is worth advancing
### 4) Work from `Ready` for applications
`ready` jobs are the primary application queue.
These jobs already have tailored PDFs generated for the specific job description, using the workflow described in [Reactive Resume](../features/reactive-resume).
At this stage:
1. Open job details.
2. Optionally enable tracer links for that specific job.
3. Download the tailored PDF.
4. Submit your application externally.
### 5) Mark jobs as applied in JobOps
After submitting, return to JobOps and mark the job as `applied`.
Effects:
- job moves to the `applied` state
- configured completion webhook(s) are triggered
- job is included in overview analytics
This completes the detailed pre-application loop.
## What happens next
Once a job is marked `applied`, it becomes part of:
- pipeline outcome analytics on [Overview](../features/overview)
- optional post-application workflows (inbox/review routing)
## Practical tips
- Start with conservative run sizes while tuning sources.
- Increase tailored-job count only after score thresholds feel calibrated.
- Expect scraper runtime variance by source.
- Keep resume/project context up to date so scoring/tailoring quality stays high.
- Use per-job tracer links when you want measurable outbound-link analytics.
- If you use tracer links, review the risk note in [Tracer Links](../features/tracer-links): some recipients/security tools may treat redirects as suspicious.
## Related pages
- [Orchestrator](../features/orchestrator)
- [Reactive Resume](../features/reactive-resume)
- [Settings](../features/settings)
- [Overview](../features/overview)
- [Post-Application Workflow](./post-application-workflow)
- [Post-Application Tracking](../features/post-application-tracking)

View File

@ -0,0 +1,145 @@
---
id: post-application-workflow
title: Post-Application Workflow
description: Track post-application progress manually, or configure automatic Gmail syncing and inbox review.
sidebar_position: 2
---
## Goal
After a job is marked `applied`, use this workflow to track what happens next.
You have two valid paths:
- **Manual tracking**: update stages/events yourself.
- **Automatic Gmail sync**: let email ingestion route events into inbox/review flow.
## Option A: Manual event tracking
Use this when you want explicit, hands-on control for each job.
### Manual flow
1. Open an `applied` or `in_progress` job.
2. Record stage progress as events (screening, interview, offer, closed, etc.).
3. Keep notes/outcomes current as conversations progress.
4. Use In Progress Board for high-attention jobs in later stages.
### API example (manual stage transition)
```bash
curl -X POST "http://localhost:3001/api/jobs/<jobId>/stages" \
-H "content-type: application/json" \
-d '{
"toStage": "technical_interview",
"metadata": {
"actor": "user",
"eventType": "status_update",
"eventLabel": "Moved to Technical Interview"
}
}'
```
## Option B: Automatic Gmail syncing
Use this when you want JobOps to ingest recruitment emails and suggest/apply updates.
### High-level flow
1. Connect Gmail provider.
2. Run sync (or scheduled sync, depending on setup).
3. Smart router scores message-to-job match.
4. High confidence updates are auto-linked.
5. Mid/low confidence items go to inbox for review.
### Configure Gmail sync
Set OAuth variables:
```bash
GMAIL_OAUTH_CLIENT_ID=...
GMAIL_OAUTH_CLIENT_SECRET=...
GMAIL_OAUTH_REDIRECT_URI=https://your-domain.com/oauth/gmail/callback
```
For exact Google Cloud steps and scope requirements, see:
- [Gmail OAuth Setup](/docs/next/getting-started/gmail-oauth-setup)
Then in app:
1. Open Tracking Inbox / provider controls.
2. Start Gmail OAuth.
3. Complete consent.
4. Trigger sync and review inbox items.
### API examples (Gmail path)
```bash
# Start OAuth
curl "http://localhost:3001/api/post-application/providers/gmail/oauth/start?accountKey=default"
```
```bash
# Exchange authorization code
curl -X POST "http://localhost:3001/api/post-application/providers/gmail/oauth/exchange" \
-H "content-type: application/json" \
-d '{"accountKey":"default","state":"<state>","code":"<code>"}'
```
```bash
# Trigger provider sync action
curl -X POST "http://localhost:3001/api/post-application/providers/gmail/actions/sync" \
-H "content-type: application/json" \
-d '{"accountKey":"default","maxMessages":100,"searchDays":30}'
```
```bash
# Review inbox
curl "http://localhost:3001/api/post-application/inbox?provider=gmail&accountKey=default"
```
```bash
# Approve inbox item
curl -X POST "http://localhost:3001/api/post-application/inbox/<messageId>/approve" \
-H "content-type: application/json" \
-d '{"provider":"gmail","accountKey":"default"}'
```
```bash
# Deny inbox item
curl -X POST "http://localhost:3001/api/post-application/inbox/<messageId>/deny" \
-H "content-type: application/json" \
-d '{"provider":"gmail","accountKey":"default"}'
```
## Which option should you use?
- Choose **manual** if your volume is low and you want direct control.
- Choose **automatic Gmail sync** if your volume is higher and you want less repetitive triage.
- Many users combine both: auto-sync first, manual adjustments for edge cases.
## Common problems
### Gmail connected but no messages appear
- Verify OAuth credentials and redirect URI.
- Confirm you are syncing the intended account key.
- Check search window (`searchDays`) and message cap (`maxMessages`).
### Wrong job matched
- Expected in lower-confidence buckets.
- Deny incorrect inbox items and apply manual stage updates where needed.
### I prefer not to grant Gmail access
- Use the manual tracking path only.
- The post-application workflow still works without Gmail integration.
## Related pages
- [Find Jobs and Apply Workflow](./find-jobs-and-apply-workflow)
- [Post-Application Tracking](../features/post-application-tracking)
- [In Progress Board](../features/in-progress-board)
- [Overview](../features/overview)

View File

@ -0,0 +1,74 @@
{
"docsSidebar": [
"intro",
{
"type": "category",
"label": "Getting Started",
"items": [
"getting-started/self-hosting",
"getting-started/gmail-oauth-setup"
]
},
{
"type": "category",
"label": "Workflows",
"items": [
"workflows/find-jobs-and-apply-workflow",
"workflows/post-application-workflow",
"workflows/add-an-extractor"
]
},
{
"type": "category",
"label": "Core Features",
"items": [
"features/overview",
"features/pipeline-run",
"features/job-search-bar",
"features/keyboard-shortcuts",
"features/multi-select-and-bulk-actions",
"features/orchestrator",
"features/settings",
"features/reactive-resume",
"features/in-progress-board",
"features/ghostwriter",
"features/post-application-tracking",
"features/visa-sponsors",
"features/tracer-links"
]
},
{
"type": "category",
"label": "Extractors",
"items": [
"extractors/overview",
"extractors/gradcracker",
"extractors/jobspy",
"extractors/adzuna",
"extractors/hiring-cafe",
"extractors/manual",
"extractors/ukvisajobs"
]
},
{
"type": "category",
"label": "Self-Hosting & Ops",
"items": [
"getting-started/self-hosting",
"getting-started/gmail-oauth-setup",
"getting-started/database-backups",
"troubleshooting/common-problems"
]
},
{
"type": "category",
"label": "Troubleshooting",
"items": ["troubleshooting/common-problems"]
},
{
"type": "category",
"label": "Reference / FAQ",
"items": ["reference/faq", "reference/documentation-style-guide"]
}
]
}

View File

@ -1 +1,11 @@
["0.1.27", "0.1.26", "0.1.25", "0.1.24", "0.1.23", "0.1.22", "0.1.21", "0.1.20"]
[
"0.1.28",
"0.1.27",
"0.1.26",
"0.1.25",
"0.1.24",
"0.1.23",
"0.1.22",
"0.1.21",
"0.1.20"
]