AI Automation · Browser Extension · Job Automation
SeamlessApply
AI-Powered Bulk Job Application Platform
Built a full-stack AI-powered job application automation platform that scrapes jobs from Indeed, LinkedIn, Workday, and Remote — then uses GPT-4 to auto-fill application forms, generate personalized cover emails, and apply to hundreds of jobs on autopilot. Includes a Chrome extension, cron automation, and an admin dashboard.
- Role
- Solo Full-Stack Developer
- Year
- 2025
- Type
- Full-Stack SaaS · AI Integration · Browser Extension · Job Automation
- 60+
- API endpoints
- 12+
- UI pages
- 12
- Database models
- 6
- External integrations
Quick summary
Job seekers create "Seam" campaigns — defining job title, location, experience level, and target boards — and the system automatically scrapes matching jobs from Indeed, LinkedIn, Workday, and Remote. GPT-4 fills application forms, generates personalized cover-letter emails, and the system applies to jobs automatically via a daily cron job or through a Chrome extension installed on the user's browser.
The problem
Job searching at scale is painful:
- Manually searching 4+ job boards wastes hours daily
- Every application form asks the same questions, typed from scratch each time
- Writing a unique cover letter per company is time-consuming
- Most people apply to too few jobs because the process is exhausting
- No way to track which jobs you've already applied to
The goal: build a platform that finds jobs, fills forms, writes emails, and applies automatically — while the user is asleep.
My role
Solo full-stack developer, end-to-end ownership: product architecture and database design, UI/UX and frontend (12+ pages), backend API (60+ endpoints), a separate Chrome Extension build, third-party integrations (OpenAI, Apify, GCS, AWS S3, Google Maps, Nodemailer), the authentication system, cron automation, and production deployment.
Core concept: the "Seam"
The central entity is the Seam — a job search campaign. Each seam stores a job title, location and radius, job type, experience level, target boards (Indeed, LinkedIn, Workday, Remote, or all), an uploaded resume (parsed via GPT-4 from a GCS-stored PDF), settings (excluded companies/keywords, auto-apply toggle, AI answering toggle), and a personalized email template. Users can run multiple active seams simultaneously.
What was built
- Seam Management — create/update/delete seams; resume upload to GCS with GPT-4 text extraction.
- Job Scraping & Results — Apify-triggered multi-board scraping into a
ScrapedJobtable; filterable, paginated results (18/page); polling detects criteria changes and prompts a refresh. - AI Form Filling — the Chrome extension detects form fields on a job site, sends them to
/api/ai-fill-fields, and GPT-4 returns field-specific answers, with a regex fallback if parsing fails. - Indeed Q&A Learning System — the extension detects Indeed application questions; unseen questions get an AI-generated answer the user confirms once, then every future occurrence of that field is pre-filled instantly with no AI call.
- Auto-Apply via Email — per-seam email templates with variable placeholders; company-email lookup (cached first, API fallback); strict deduplication so no company is ever emailed twice per seam.
- Cron Automation — a daily unattended run (max 5-minute execution) processes every seam with auto-send enabled, applying the same match logic as the UI, with per-seam error isolation.
- AI Email Template Generator — GPT generates a full template from job title, experience, and a formality preference (formal / semi-formal / casual).
- CV/Resume Extraction — PDF upload to GCS, then GPT-4 Vision parses it into structured data (name, skills, experience, education) used to personalize form answers.
- Authentication — NextAuth.js for the web app; a separate JWT path (stored in
localStorage) for the Chrome extension, since extensions can't use httpOnly cookies across origins. - Admin Panel — user stats, subscriber growth and jobs-matched charts, client management, role-gated routes.
Core features — technical deep dive
Shared job-matching logic. The isJobMatch() function lives in one shared module (utils/job-matching.ts) and is imported by both the frontend results page and the cron auto-apply job — so the same criteria (excluded companies/keywords, board, location) are applied identically everywhere, and a fix in one place fixes both paths at once.
Dual authentication for a cross-origin extension. The web app uses NextAuth sessions (httpOnly cookies), which don't work across the extension's different origin. The extension instead authenticates against /api/auth/login, receives a JWT, stores it in extension storage, and sends it as an Authorization: Bearer header — extension-facing endpoints extract and verify that token manually. Both paths hit the same database and the same endpoints.
Application deduplication across manual and automated paths. Every apply attempt — whether the user clicks Apply manually or the cron job fires — checks JobApplication for an existing record for that job/seam pair first. The table is the single source of truth, so a job never gets a duplicate application regardless of which path triggered it.
Indeed Q&A matching on field ID, not question text. Indeed sometimes renders the same question with slightly different wording, so matching is done on the DOM's fieldId (which is consistent for a given question type) instead of the label text, with a @@unique([userId, fieldId]) constraint enforcing one saved answer per field per user.
Cron job resilience at scale. maxDuration = 300, a 1-second delay between individual sends and between seams, and a try/catch wrapped around each seam individually — one seam's failure never stops the rest from processing.
// /api/cron-auto-apply — simplified
for (const seam of seamsWithAutoSend) {
try {
const jobs = await prisma.scrapedJob.findMany({
where: { seamId: String(seam.id), createdAt: { gte: oneWeekAgo } },
});
const unapplied = jobs.filter((j) => !appliedIds.has(j.id));
const matched = unapplied.filter((job) => isJobMatch(job, seamData));
for (const job of matched) {
const email = await findCompanyEmail(job.company, job.location);
if (email) await sendApplicationEmail(job, seam.id, email, template, user);
await sleep(1000);
}
} catch (error) {
ErrorTracker.trackError(error, "CRON_AUTO_APPLY_API", { seamId: seam.id });
}
}
Database schema (key models)
User— auth, roleJobSubmission(the "Seam") — search criteria, resume text, board targetsSeamSettings— auto-send/auto-fill/AI-answering toggles, match threshold, exclusion listsScrapedJob— one row per scraped listing, tagged by source boardJobApplication— application record with status (applied/interviewed/rejected/offered)EmailTemplate— formal/semi-formal/casual templates per seamIndeedQuestion— learned Q&A pairs, unique per user + fieldCompanyEmail— cached/discovered company email addresses
Key numbers
| Metric | Value |
|---|---|
| Job boards integrated | 4 (Indeed, LinkedIn, Workday, Remote) |
| API endpoints | 60+ |
| UI pages | 12+ |
| Database models | 12 |
| AI integrations | OpenAI GPT-4 + GPT-3.5 |
| External integrations | 6 (OpenAI, Apify, GCS, AWS S3, Nodemailer, Google Maps) |
What this demonstrates
- API integration expertise — 6 third-party APIs integrated with proper auth, pagination, rate-limit, and error handling for each; a dual GCS/S3 storage strategy.
- AI/LLM product building — three distinct AI use cases (form filling, Q&A answering, CV parsing) with caching and fallback pipelines for unpredictable GPT output.
- Browser extension development — a Manifest V3 extension in React/TypeScript/Vite with cross-origin API communication and DOM-level form automation on third-party sites.
- Production-level backend — shared business logic between frontend and backend, structured logging and error tracking, cron automation with isolation, rate limiting, and deduplication.
SeamlessApply — job application automation platform. Built end-to-end as a solo developer.