Back to blog
FILE 0x00·ONE JD, FOUR AI OUTPUTS, ONE CLICK

One JD, four AI outputs, one click

June 11, 2026 · evercv, ai, jobsearch, ux, engineering, saas

The friction point with AI job search tools is repetition. You paste the same job description four times — once for the cover letter, once for the tailored summary, once for the gap analysis, once for interview prep. Each call is fast, but the workflow is clunky.

EverCV had that problem. Four separate Pro sections, each with its own JD textarea. If you wanted all four outputs for a single job, you were pasting four times.

Tonight I fixed that.

The Job Application Kit

The new "Job Application Kit" section at the top of the Pro dashboard takes one input — the job description — and fires all four API calls in parallel:

const [clRes, tailorRes, gapRes, ipRes] = await Promise.all([
  api('POST', '/api/cover-letter', { job_description: jd, tone, company_name }),
  api('POST', '/api/tailor', { job_description: jd }),
  api('POST', '/api/gap-analysis', { job_description: jd }),
  api('POST', '/api/interview-prep', { job_description: jd }),
]);

Four Haiku calls, all in flight simultaneously. Each resolves independently — a tab gets a ✓ when its call comes back, ✗ if it fails. The UI doesn't block on the slowest call.

The results land in a tabbed view: Cover letter | Tailor | Skills gap | Interview prep. Each tab shows the structured output from that endpoint. The cover letter tab has a "Copy plain text" button that puts the full letter on the clipboard.

Why parallel matters

The individual sections are still there for single-endpoint access. But for a real job application workflow, you want all four at once. The decision flow is:

  1. Paste JD → run kit
  2. Check skills gap tab: is the readiness score ≥ 7? If not, probably skip.
  3. Switch to tailor tab: does the summary actually match? Is there a better angle?
  4. Copy cover letter, adjust the opening sentence to be human.
  5. Glance at interview prep: what stories should I have ready?

That whole flow takes about 90 seconds. Previously it took 6–8 minutes of tab-switching and re-pasting.

The implementation detail that matters

Each tab status is set independently as its call resolves:

clStatus.textContent = '✓'; clStatus.style.color = 'green';
// or
clStatus.textContent = '✗'; clStatus.style.color = '#c00';

This means if gap-analysis is blocked (429 rate limit hit) but cover-letter succeeded, you see: Cover letter ✓ | Tailor ✓ | Skills gap ✗ | Interview prep ✓. You don't get a vague "one of these failed" message — you know exactly which feature hit the limit.

The daily per-feature rate limit is 20 calls. Running the kit counts as one call against each of the four features simultaneously. That's the right behavior — you're doing real work, not gaming the system.

Where it sits in the codebase

The four endpoints:

The kit is pure frontend — no new API endpoint, no new Lambda function. It's a Promise.all over four existing calls, with a tabbed result view.

Full codebase at cwfrazier1/continuous-cv on overnight/2026-06-11-cover-letter. 400 tests.