§ unswayed-backend · API contract & docs
Lexi AI — interview coaching (Phase 14, UN-112)
What it is
A mock-interview loop (src/lexi-ai/interview-coaching/): the applicant starts a session for a
job, answers AI-generated questions one at a time, gets per-answer feedback, and finishes with an
overall score. Per-user Lexi /api/v1/*, behind the interviewCoaching feature flag.
The loop
POST /api/v1/users/{userId}/interview-coaching/sessions— 10/hour. Body{jobPostingId | jobDescriptionText, focusAreas?}(focus ⊆ technical/behavioral/situational/ culture_fit). The LLM generates the first question grounded in the JD + focus areas; the session is createdin_progress(defaultmaxQuestions=5) with the firstInterviewCoachingResponse(ordinal 1, unanswered). Returns{sessionId, status, currentQuestion:{questionId, category, questionText}}.POST .../sessions/{id}/responses— 60/hour. Body{questionId, responseText}(≥20 chars, else 422). Validates the session is owned +in_progress(completed → 422 read-only) and thatquestionIdis the current unanswered question. The LLM evaluates the answer →feedback:{score, strengths[], improvementAreas[]}+responseScore. If more questions remain it generates the next one and returns{feedback, nextQuestion}; on the last answer it completes the session (overallScore= average of response scores,status:completed,nextQuestion:null).GET .../sessions/{id}— summary with each response's score + improvementAreas (404 if not owned).GET .../sessions— paginated history, newest first.
How it works
Questions and answers are rows in InterviewCoachingResponse (the row id is the questionId
handed to the client); the session tracks status + the running overallScore. The slice reuses the
shared AI seam (gpt-4o-mini, JSON + retry-once, LexiUnavailableError → 503) and the per-user
ownership/feature primitives. It deliberately does not read the employer-side Lenux
interview_questions table — only shared prompt patterns, never shared data — keeping the
applicant and recruiter sides isolated.