Let's Build: AI resume parser with Transloadit
Resume parsing is a classic SaaS problem: take messy, real-world documents and extract structured fields that feed ATS pipelines. With Transloadit, you can build that workflow from primitives in a single assembly.
What we’re building
- Accept PDF, DOCX, or image resumes
- Convert non-PDFs to PDF with 🤖 /document/convert
- Extract fields with 🤖 /ai/chat
Schema first (zod)
import { z } from 'zod'
import { zodToJsonSchema } from 'zod-to-json-schema'
const resumeSchema = z.object({
full_name: z.string().optional(),
headline: z.string().optional(),
email: z.string().optional(),
phone: z.string().optional(),
location: z.string().optional(),
summary: z.string().optional(),
skills: z.array(z.string()).optional(),
work_experience: z
.array(
z.object({
company: z.string().optional(),
title: z.string().optional(),
start_date: z.string().optional(),
end_date: z.string().optional(),
highlights: z.array(z.string()).optional(),
}),
)
.optional(),
education: z
.array(
z.object({
institution: z.string().optional(),
degree: z.string().optional(),
start_date: z.string().optional(),
end_date: z.string().optional(),
}),
)
.optional(),
})
const toJsonSchema = (schema: z.ZodTypeAny): unknown =>
typeof (z as { toJSONSchema?: typeof zodToJsonSchema }).toJSONSchema === 'function'
? (z as { toJSONSchema: typeof zodToJsonSchema }).toJSONSchema(schema)
: zodToJsonSchema(schema)
const resumeJsonSchema = toJsonSchema(resumeSchema)
Pipeline overview
PDF resumes ──▶ /ai/chat
Other docs ──▶ /document/convert (pdf) ──▶ /ai/chat
Assembly snippet
const assembly = await transloadit.createAssembly({
params: {
steps: {
pdf_verified: {
robot: '/file/filter',
use: ':original',
accepts: [['${file.mime}', '==', 'application/pdf']],
},
non_pdf: {
robot: '/file/filter',
use: ':original',
accepts: [['${file.mime}', '!=', 'application/pdf']],
},
pdf_converted: {
robot: '/document/convert',
use: 'non_pdf',
format: 'pdf',
},
extract_resume: {
robot: '/ai/chat',
use: ['pdf_verified', 'pdf_converted'],
model: 'anthropic/claude-4-sonnet-20250514',
format: 'json',
schema: JSON.stringify(resumeJsonSchema),
messages: 'Extract resume fields. Omit keys if unknown.',
result: true,
},
},
},
files: {
document: filePath,
},
waitForCompletion: true,
})
Try the example app
The runnable version of this post lives at example_apps/resume-parser/run.ts.
node example_apps/resume-parser/run.ts
Suggested inputs for visuals
Use the bundled samples or swap in a real resume PDF:
_assets/demos/inputs/first_document.pdf_assets/demos/inputs/second_document.pdf_assets/demos/inputs/scan.pdf
Next steps
- Add a normalization layer for phone numbers and dates.
- Flag missing fields and route to manual review.
- Store results in a talent database or ATS.
