
Own product · 2026
CraftlyOne CV, Rewritten for Every Job You Apply To
Upload a PDF, paste a job post, get an ATS-ready CV in the employer's own language.
- Own product
- —
- Year
- 2026
- Role
- Design + Full-stack
- Services
- Product design, Full-stack, AI
- Stack
- Next.js · React · TypeScript · Tailwind CSS · Motion · NextAuth · Prisma · PostgreSQL · Google Gemini · Groq · Zod · unpdf · Vercel
The challenge
Rewrite a CV for every job without inventing a single employer, degree or date, and run the whole product on free tiers.
The idea
Upload a PDF once. After that every application is a paste and about a minute: a CV in the employer's own language, plain enough for applicant tracking systems, plus drafted screening answers.
Read the full build notes
Craftly takes the CV you already have plus a job posting and rewrites the CV in the employer's own language — the same truth, aimed properly — then drafts the answers to the screening questions on the application form. Upload a PDF once and it becomes a structured, editable profile; after that every application is a paste and about a minute. The export is deliberately plain: single-column A4 typeset in Computer Modern, no colour and no icons, because that is what applicant tracking systems parse cleanly and what reads like a document a person actually wrote.
The whole thing runs on free tiers, and that constraint set most of the engineering. Model calls fan out across Gemini and Groq under one shared 50-second budget that sits below the serverless ceiling, each attempt capped at 22 seconds, and a new attempt is only started if four seconds remain — an earlier version could chain six models with retries and outlive the function, returning nothing at all. Model ids are pinned rather than aliased and can be overridden from the environment, because providers retire them on their own schedule and a hardcoded list turns that into a silent outage. A retired model, a rate limit and a timeout are distinguished from one another and reported as such, instead of every failure becoming 'temporarily unavailable' forever.
Both providers are asked for JSON natively, and the response is still pulled apart by a scanner that walks balanced delimiters while respecting string literals — an indexOf/lastIndexOf slice breaks on any trailing prose containing a brace. Output is validated with Zod and retried once, with the parse failure fed back into the prompt.
Quota is enforced per user against the database rather than a second service. The claim row is written before the count is read: a check-then-write lets a double-click or a duplicated tab both read the same count, both pass, and both spend upstream free-tier quota, which is the one thing the limiter exists to prevent. A rejected claim is rolled back immediately, and expired rows are pruned opportunistically on two per cent of requests instead of by a cron job. A PDF upload is charged only once the file has yielded usable text, since parsing is local and free and an unreadable scan should not cost the user a slice of their day.
Prompts live outside the route handlers. Earlier versions serialised the entire history — every full tailored CV — into each request, so prompt size grew without bound until it blew past the free-tier token limits; now only a capped digest of the four most recent applications is sent, enough to keep employers, titles and dates consistent while the bullet prose is deliberately rewritten per role. The rewrite is bounded on purpose: the model is told never to invent an employer, a degree or a date, never to claim a technology with no basis in the candidate's record, and never to reach for 'passionate about' or 'results-driven'. Auth is NextAuth v5 over Google, with the edge proxy running an adapter-free config so identity comes from the JWT alone, and an unauthenticated API call returns a JSON 401 rather than redirecting to an HTML page that would break a fetch().json() on the client. The interface is warm paper — a cream canvas, one orange accent, hairlines instead of shadows, three typefaces each with a single job, and hand-authored SVG illustrations so there is no icon library anywhere in the project. Roughly 7,700 lines across 60 source files, 10 API routes and 6 pages.

Key screens



In motion
On the phone



Start a project