Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nayalsaurav/ai360/llms.txt

Use this file to discover all available pages before exploring further.

The resume-analyzer endpoint accepts a multipart form submission containing a PDF resume and job context. It uses Gemini 2.5 Flash with a structured JSON schema (responseSchema) to produce a deterministic, type-safe evaluation across five categories: ATS compatibility, tone and style, content relevance, structure and formatting, and skills presentation. Each category receives a numeric score (0–100) alongside 3–4 actionable tips that distinguish positive observations from areas that need improvement.

Request

Method: POST
Path: /api/utilities/resume-analyzer
Content-Type: multipart/form-data
companyName
string
required
The name of the target company. Used to tailor the AI prompt with hiring context.
jobTitle
string
required
The exact title of the position being applied for (e.g., Senior TypeScript Engineer).
jobDescription
string
required
The full text of the job description. The AI uses this to judge keyword alignment and skills relevance.
file
File
required
The candidate’s resume. Must be a PDF (application/pdf) and no larger than 10 MB.

Validation Rules

All inputs are validated server-side by validateInputs before any AI call is made:
  • companyName, jobTitle, and jobDescription must each be non-empty after trimming whitespace.
  • file must be present, have MIME type application/pdf, and be 10 MB or less (file.size > 10 * 1024 * 1024 triggers an error).
  • When any rule is violated the function throws, and the outer handler returns HTTP 500 with { success: false, error: "..." }.
All validation failures return 500, not 400 — this is intentional in the source implementation. Clients should treat any non-success response as an error regardless of status code.

Response

TypeScript Interfaces

interface FeedbackTip {
  type: 'good' | 'improve'
  tip: string
  explanation?: string  // present for toneAndStyle, content, structure, skills
}

interface FeedbackSection {
  score: number  // 0–100
  tips: FeedbackTip[]
}

interface Feedback {
  overallScore: number
  ATS: FeedbackSection
  toneAndStyle: FeedbackSection
  content: FeedbackSection
  structure: FeedbackSection
  skills: FeedbackSection
}

// API response
interface ResumeAnalyzerResponse {
  success: boolean
  feedback: Feedback
}
The explanation field is required in the schema for toneAndStyle, content, structure, and skills tips, but is optional (omitted) for ATS tips.

Response Fields

success
boolean
true when the request completed without a thrown error. Note that a true value does not guarantee a meaningful analysis — see the silent failure note below.
feedback
Feedback
The full structured analysis object.

Success Response (200)

{
  "success": true,
  "feedback": {
    "overallScore": 78,
    "ATS": {
      "score": 82,
      "tips": [
        { "type": "good", "tip": "Resume contains relevant keywords from the job description" },
        { "type": "improve", "tip": "Add more quantified achievements" }
      ]
    },
    "toneAndStyle": {
      "score": 75,
      "tips": [
        { "type": "good", "tip": "Professional and confident tone throughout", "explanation": "Active voice and strong action verbs reinforce a commanding presence." },
        { "type": "improve", "tip": "Avoid first-person pronouns", "explanation": "Phrases like 'I managed' should be replaced with 'Managed' for standard resume convention." }
      ]
    },
    "content": { "score": 80, "tips": ["..."] },
    "structure": { "score": 72, "tips": ["..."] },
    "skills": { "score": 85, "tips": ["..."] }
  }
}

Error Responses (500)

TriggerResponse body
Missing or blank companyName{ "success": false, "error": "Company name is required" }
Missing or blank jobTitle{ "success": false, "error": "Job title is required" }
Missing or blank jobDescription{ "success": false, "error": "Job description is required" }
File is not a PDF{ "success": false, "error": "Only PDF files are supported" }
File exceeds 10 MB{ "success": false, "error": "File size must be less than 10MB" }
Unexpected server error{ "success": false, "error": "Internal Server Error" }
Silent failure: If Gemini processing fails after successful validation (e.g., an empty AI response or a JSON parse error), the endpoint still returns HTTP 200 with success: true. The feedback.overallScore will be 0 and every category’s tips array will contain a single improve tip stating "Unable to analyze …". This is the createErrorFeedback() path in the source — always check overallScore to detect a degraded response.

Example

curl -X POST http://localhost:3000/api/utilities/resume-analyzer \
  -F "companyName=Acme Corp" \
  -F "jobTitle=Senior TypeScript Engineer" \
  -F "jobDescription=We are looking for a TypeScript engineer with 5+ years of experience..." \
  -F "file=@/path/to/resume.pdf"

Implementation Notes

  • PDF encoding: The uploaded file is read as an ArrayBuffer, converted to a Buffer, and serialized to base64. It is passed to Gemini as inlineData with mimeType: "application/pdf" — the model reads the raw PDF bytes directly.
  • Structured output: The Gemini call sets responseMimeType: "application/json" and provides a responseSchema built from @google/genai’s Type helpers. This guarantees the response matches the Feedback interface without any ad-hoc parsing.
  • Model override: Set GOOGLE_AI_MODEL in your environment to use a different Gemini model. If unset, the endpoint defaults to gemini-2.5-flash.
  • API key: This endpoint initializes GoogleGenAI with process.env.GOOGLE_API_KEY. It does not use GEMINI_API_KEY.
This endpoint uses GOOGLE_API_KEY, not GEMINI_API_KEY. Other AI360 endpoints may rely on GEMINI_API_KEY instead. Make sure both variables are present in your .env.local file if you are running multiple AI features simultaneously — a missing GOOGLE_API_KEY will cause every resume analysis request to fail at the Gemini initialization stage.

Build docs developers (and LLMs) love