About BuffMatch AI

Inspiration

Applying to on-campus jobs at CU Boulder often feels like sending resumes into a void. Students rarely hear back, and when they do get rejected, they usually receive no explanation—no feedback on what was missing, what skills to develop, or how to improve their next application. Recruiters, meanwhile, spend hours sifting through applications without a clear way to rank candidates or explain their decisions.

We built BuffMatch AI to change that. Our goal was to create an explainable ATS (Applicant Tracking System) specifically for CU Boulder on-campus jobs—one that gives students transparent feedback on why they match or don't, and helps recruiters make faster, fairer hiring decisions with AI-powered scoring and structured applicant tracking.


What it does

For students:

  • Upload a PDF resume and get AI-parsed skills, experience, education, email, and graduation date
  • Browse on-campus jobs with match scores and a breakdown by category (eligibility, skills, experience, availability)
  • See gap analysis when screened out: what was missing, how to improve, and up to 3 alternate job recommendations
  • Receive email notifications when rejected (auto-screened or by recruiter), with actionable feedback
  • Access hiring contact info (name, email, phone, office hours) for each job

For recruiters:

  • Post jobs with full descriptions and contact details
  • View ranked applicants (only those with score (\geq 65\%)) with score breakdowns
  • Use blind review mode to hide applicant names for fairer evaluation
  • Manage applicants with a tracker (Applied, Under review, Shortlisted, Rejected, Hired)
  • Reject applicants with AI-generated feedback that is automatically emailed to the student

How we built it

We built BuffMatch AI as a full-stack Next.js 13 application with the App Router, TypeScript, and Prisma (SQLite) for zero-friction local development.

Layer Technology
Frontend Next.js 13.5, React 18, Tailwind CSS (CU Boulder theme)
Backend Next.js API routes, Prisma ORM
Database SQLite (file-based, no external DB needed)
Auth NextAuth.js (credentials, @colorado.edu only)
AI OpenAI GPT-4o-mini (resume/job parsing, rejection summaries)
Email EmailJS (rejection notifications)

Core flow:

  1. Resume parsing: Extract text from PDF with pdf-parse, then use OpenAI to parse skills, experience, education, email, and graduation date. Keyword fallback when API key is missing.
  2. Match scoring: Compute a weighted score (S_{\text{overall}}) from eligibility, skills, experience, availability, and preferred skills. Hard filters (work-study, hours, availability) must pass.
  3. Auto-screening: Applications with (S_{\text{overall}} < 65\%) or failed hard filters are marked screened_out. AI generates a rejection summary, we store feedback, and send an email via EmailJS.
  4. Recruiter workflow: Recruiters see only applicants with (S_{\text{overall}} \geq 65\%), ranked by score. Rejecting triggers the same feedback + email flow.

Challenges we ran into

  1. Session and fetch failures in embedded browsers. Cursor's preview and some embedded browsers block fetch calls, causing "Failed to fetch" when NextAuth validated sessions. We fixed this by aligning NEXTAUTH_URL with the actual port and switching sign-out to a redirect-based flow instead of client-side fetch.

  2. "Missing required error components" in Next.js. Running multiple next dev instances or stale .next cache caused Next.js to fail when rendering error pages. We added error.tsx, global-error.tsx, not-found.tsx, and Pages Router fallbacks (pages/_error.js, pages/404.js).

  3. EmailJS configuration. Rejection emails weren't sending because env vars were unset, the template needed {{to_email}} in the To field, and dynamic recipient logic could yield empty addresses. We added logging, fallbacks, and proper template setup.

  4. Screened-out applications not displaying correctly. The UI checked trackerStatus for "screened_out", but we only set status and left trackerStatus as "applied". We updated creation logic to set trackerStatus: "screened_out" when auto-rejecting.

  5. Resume email extraction. Students expect rejection emails at the address on their resume. We extended the parser to extract resumeEmail and graduationDate and surface them in the parsed profile UI.


Accomplishments that we're proud of

  • Explainable ATS from scratch: Built a full match engine with hard filters, weighted scoring, and AI-generated rejection summaries—no black box.
  • End-to-end rejection flow: Auto-screening, recruiter rejection, feedback storage, and email notifications all work together.
  • Graceful degradation: App works without OpenAI (keyword-based parsing) and without EmailJS (feedback still shown in-app).
  • CU Boulder–specific design: @colorado.edu auth, work-study eligibility, and job types tailored to on-campus roles.
  • Clean UX: Score breakdowns, gap analysis, alternate job recommendations, and blind review mode for recruiters.

What we learned

  • Explainability matters. Black-box AI that silently rejects applicants frustrates users. Surfacing match scores, gap analysis, and improvement suggestions turned rejections into learning opportunities.
  • Context-aware matching is hard. On-campus jobs have unique constraints: work-study eligibility, hours per week, availability. We combined hard filters with soft scoring so candidates pass or fail for clear, defensible reasons.
  • Resume parsing is non-trivial. PDFs vary wildly. We used OpenAI for intelligent extraction with a keyword fallback—ensuring the app works even without an API key.
  • Email delivery has many failure modes. Template variables, CORS, and fallbacks when emails fail all mattered.

What's next for BuffMatch AI

  • Handshake integration: Sync jobs from Handshake once auth/cookie restrictions are resolved
  • Bulk applicant actions: Recruiters could shortlist or reject multiple applicants at once
  • Email templates per role: Different rejection templates for auto-screening vs. recruiter rejection
  • Analytics dashboard: Match score distributions, application funnel metrics, and recruiter insights

Built With

Share this project:

Updates