Save any job posting to your Utably application tracker with one click.
A privacy-first, open source browser extension for Chrome, Edge, Firefox, and Safari.
What it does · Install · Contribute · FAQ · Docs
You're on a job posting. You click the Utably toolbar icon. A side panel slides in with the title, company, location, and description already filled out. You review, optionally run an AI fit check against your profile, and click Save. That's the whole extension.
- One-click import from 15+ job boards including LinkedIn, Indeed, Glassdoor, ZipRecruiter, Google Careers, and most Greenhouse/Lever ATS pages.
- AI FitCheck — a traffic-light score, skills breakdown, and qualification analysis for how well a posting matches your Utably profile.
- Profile autofill — fill the contact, employment, and education
fields on a Greenhouse, Lever, or Ashby application form from your
Utably profile. You see exactly which fields go to which host before
any data leaves the extension; the page must not change between
preview and fill (see
docs/fill.md). - Saved tab — a third side-panel tab listing your imported applications. Filter by Saved / Applied / Interview, search across title / company / location / source, change status with optimistic write-back, copy fields, or open the application in the Utably web app. Click the FitCheck score chip on any card to re-run or view the analysis in the same modal the Import tab uses.
- Attachments — your stored CVs, certificates, and references
surface as cards in My profile. Each card can either upload the
file directly into a matching
<input type="file">on the active page (with a fall-back place mode for custom Workday-style drop zones) or download the file to disk. - Text Capture — select any text on a page, hit copy, and drop it into a form field via a floating card.
- Side panel UI — no popups, no new tabs, never steals focus.
- Privacy-first — reads only the tab you opened, only when you click Auto-fill, never sends data to Utably without an explicit Save, and never sends profile data to a third-party site without an explicit per-host consent.
Browser extensions can, in principle, read every page you visit. "Trust us" is not a good answer to that concern — you should be able to verify what the extension actually does. Three things follow from that:
- Every behavior you care about is auditable. Auth token handling
lives in
background.js. Every job-board extractor lives inwebpages/. The content-script surface is incontent/. Nothing is minified, obfuscated, or hidden. - The LLM runs server-side. FitCheck calls
POST /extension/llm— there is no third-party AI key in the extension, no OpenAI/Anthropic endpoint you didn't consent to. Seedocs/fitcheck.md. - Adapters rot fast. Job boards redesign constantly. An open codebase means anyone whose favorite site broke can fix it and send a PR — without waiting on us.
See docs/faq.md for the full rationale and
SECURITY.md for the privacy threat model.
Install from the browser stores (live after the Utably public launch):
- Chrome / Edge — Chrome Web Store link coming soon
- Firefox — AMO listing coming soon
- Safari — App Store listing coming soon
Heads up: You need a Utably account to actually save jobs. The extension is a thin client for utably.com — it's not useful against other services without the backend. See Fork & self-host below.
git clone https://github.com/utably/utably-browser-plugin.git
cd utably-browser-plugin
npm run build:chromeThen in Chrome/Edge:
- Open
chrome://extensions(oredge://extensions) - Toggle Developer mode on
- Click Load unpacked and select
dist/utably-browser-plugin-chrome-edge/ - Click the Utably icon in the toolbar
Firefox and Safari build steps live in
docs/development.md. There are no runtime
dependencies — the extension is hand-rolled vanilla JS, so
npm install is a no-op and the build is pure file copying.
- Click the toolbar icon → the side panel opens.
- Not connected? Click Connect with Utably. You'll be taken to
the Utably web app to approve the connection. Tokens come back to
the extension and live in
chrome.storage.local. - Open a job posting on any supported site.
- Click Auto-fill. The first time you do this, Chrome prompts for permission to read websites — approve it, and the extension injects the adapter and populates the form.
- Review the fields — edit anything the adapter got wrong, and optionally jot anything you want to remember about the role into Further notes (free-form, never auto-filled).
- (Optional) Click FitCheck for an AI analysis of how well the posting matches your profile.
- Click Save to Utably. Done.
LinkedIn runs in manual-description mode by design: the extension
only fills metadata (title, company, location) and you paste the
description yourself. This is intentional — see
docs/fitcheck.md.
| Priority | Adapter | Source |
|---|---|---|
| 100 | linkedin.js |
LinkedIn (metadata only, manual description) |
| 95 | indeed.js |
Indeed |
| 95 | googlejobs.js |
Google Careers |
| 90 | glassdoor.js |
Glassdoor |
| 90 | ziprecruiter.js |
ZipRecruiter |
| 90 | monster.js |
Monster |
| 90 | careerbuilder.js |
CareerBuilder |
| 90 | dice.js |
Dice |
| 90 | wellfound.js |
Wellfound (AngelList) |
| 90 | handshake.js |
Handshake |
| 90 | usajobs.js |
USA Jobs |
| 90 | ukPortals.js |
UK job portals |
| 80 | atsHosted.js |
Greenhouse, Lever, Workday, and other ATS hosts |
| 80 | builtin.js |
BuiltIn |
| 10 | generic.js |
Fallback (JSON-LD JobPosting + DOM scoring) |
Your favorite site missing or broken? Open an adapter request or submit a PR — adapters are the easiest way to contribute. See the 10-minute adapter walkthrough.
We explicitly want outside contributions. The highest-leverage thing you can do is fix or add a job-board adapter, because adapters break whenever a site ships a redesign.
Good first issues:
- A site you use every day that isn't in the table above → new adapter.
- A site in the table above that's returning wrong/empty fields → fix the selectors.
webpages/generic.jscould always be smarter about boilerplate filtering and nested JSON-LD.
Before you start:
- Read
CONTRIBUTING.md— it covers the adapter anatomy, priority numbers, the testing checklist, and the PR rules. - Read the 10-minute adapter walkthrough — copy-paste-able starting point.
- Check the FAQ — it probably answers your question.
What we will merge quickly: adapter fixes, new adapters, bug fixes in popup UI, docs improvements, build-script improvements.
What needs discussion first: new host permissions, new backend endpoints, runtime dependencies, UI rewrites, changes to the auth flow. Open an issue before coding these up so you don't waste effort on something we can't take.
What we won't take: rebranded forks, re-skins, re-pointing to a
different backend (fork it instead — see Fork & self-host),
integrations with LLM providers that bypass /extension/llm.
Security issues → do not open a public issue. See
SECURITY.md.
The short version:
┌─────────────┐ inject ┌─────────────┐ extract ┌──────────┐
│ Side panel │ ─────────────▶│ Content │ ────────────▶│ Job page │
│ (popup.js) │ │ scripts │ │ │
└──────┬──────┘ │ (webpages/) │ └──────────┘
│ └─────────────┘
│ fill form + review
▼
┌─────────────┐ auth'd API ┌─────────────┐
│ Background │ ─────────────▶│ Utably API │
│ service │ │ /extension/*│
│ worker │◀──────────────│ │
└─────────────┘ tokens └─────────────┘
manifest.json— Manifest V3 declaration.background.js— service worker, auth, API calls, token rotation, profile cache (inchrome.storage.session), fill-adapter injection.popup.html+popup/— the side panel UI and its controllers. Tab controllers split out:popup/app.js(Import + FitCheck modal),popup/profile.js(My profile + attachment cards),popup/saved.js(Saved tab, status write-back, per-card FitCheck rerun).webpages/— one extract adapter per supported job board, plusrouter.jswhich picks the highest-priority match for the current page.content/capture.js— the floating text-capture card.content/fill/— fill adapters for Greenhouse, Lever, Ashby, and a top-frame-only generic fallback. Plan-verify-apply runs in one synchronous frame execution. Seedocs/fill.md.content/fill/attachments.js— DataTransfer injection of stored files into matching<input type="file">elements on the active page.content/fill/dropmode.js— fall-back drop synthesizer for sites that hide their file input behind a custom drop zone (Workday-style). User clicks a highlighted target, the script synthesizes the fulldragenter→dragover→dropsequence.scripts/— build tools (zero-dependency file copying).
Deeper dive in docs/architecture.md.
- Only reads pages you open, only when you click Auto-fill, and only on that tab.
- Host access requested at runtime via the browser's native permission prompt. The first time you click Auto-fill or Capture, Chrome asks whether the extension may read pages you visit — nothing is read until you approve. You can revoke the grant at any time in browser settings.
- No background crawling. The extension does nothing while you don't have the side panel open.
- No credential capture. The extension never touches password fields, cookies on third-party sites, or browser sync data.
- Tokens live in
chrome.storage.localwith short-lived access + rotating refresh tokens. - Profile data (used for autofill) is fetched on demand and cached
in
chrome.storage.session— in-memory only, never on disk, wiped when the browser closes. Cache TTL is 5 minutes. You can clear it manually in Settings → Autofill privacy. - Profile autofill needs explicit per-fill consent. Every fill shows a modal listing every recipient host and every field that will be filled. The page is re-checked at fill time; if it changed after you approved, the fill aborts and re-prompts.
- LinkedIn description is manual entry by design — we don't auto-scrape posting descriptions on LinkedIn.
Full threat model in SECURITY.md. Autofill design
details in docs/fill.md.
You're welcome to fork this extension, but you should know what you're signing up for. This extension is a thin client — most of the value lives in the Utably backend (profile matching, FitCheck LLM, duplicate detection, application storage). Forks must:
- Implement compatible
/extension/*endpoints on your own backend. Seedocs/api.mdfor the contract. - Replace the brand assets in
icons/andassets/— these are Utably trademarks and are not covered by the Apache-2.0 license. SeeNOTICE. - Change the extension ID and publish under your own name on the Chrome Web Store / AMO / App Store.
- Not imply endorsement by Utably.
Utably does not provide support, credentials, uptime guarantees, or adapter parity for third-party forks.
| Document | Purpose |
|---|---|
CONTRIBUTING.md |
How to contribute, PR rules, adapter checklist |
SECURITY.md |
Reporting vulnerabilities, threat model, scope |
docs/faq.md |
Common contributor and user questions |
docs/development.md |
Local setup, debugging, multi-browser build |
docs/adapters.md |
Extract adapters + 10-minute walkthrough |
docs/fill.md |
Profile autofill: flow, consent, TOCTOU protection |
docs/architecture.md |
Runtime components, auth flow, storage |
docs/api.md |
Backend API contract |
docs/fitcheck.md |
FitCheck response shape and tier gating |
docs/branching.md |
Branch strategy for external contributors |
CHANGELOG.md |
Release notes |
Code in this repository is licensed under the Apache License 2.0.
Not covered by that license: Utably trademarks, the Utably logo,
and the brand assets in icons/ and assets/. See NOTICE
for trademark terms. The Utably backend API is proprietary and is not
part of this open source release.
Built by Utably.
If this extension saved you time, contribute an adapter
— that's how it stays useful.