API reference

Base path is /api. Request and response bodies are JSON except where noted. Authentication is the token httpOnly cookie that register, login and Google sign-in set.

Every JSON response has this shape:

{ "success": true,  "data": { ... } }
{ "success": false, "error": "Human-readable message" }
  1. Health
    1. GET /api/health
  2. Auth
    1. POST /api/auth/register
    2. POST /api/auth/login
    3. POST /api/auth/google
    4. POST /api/auth/logout
    5. GET /api/auth/me πŸ”’
  3. Interview πŸ”’
    1. POST /api/interview/start
    2. POST /api/interview/answer
    3. POST /api/interview/finish
    4. GET /api/interview/history
  4. Voice πŸ”’
    1. POST /api/voice/transcribe
    2. POST /api/voice/speak

Health

GET /api/health

{ "success": true, "message": "AI Interviewer API is running." }

Unknown /api/* routes return 404 with the standard error shape.

Auth

POST /api/auth/register

{ "fullName": "Ada Lovelace", "email": "ada@example.com", "password": "at-least-6" }
Status Meaning
201 data.user = { _id, fullName, email }; session cookie set
400 missing fields, invalid email, or password shorter than 6 characters
409 email already registered

POST /api/auth/login

{ "email": "ada@example.com", "password": "..." }

200 returns data.user and sets the cookie. 401 means invalid credentials. Email matching is case-insensitive.

POST /api/auth/google

{ "credential": "<Google ID token from Google Identity Services>" }
Status Meaning
200 signed in; the account is created, or linked by verified email
401 token invalid, or the Google email is not verified
409 the email is already linked to a different Google account
503 GOOGLE_CLIENT_ID is not configured on the server

POST /api/auth/logout

Clears the cookie.

GET /api/auth/me πŸ”’

200 returns data.user. 401 means not signed in or the session expired.

Interview πŸ”’

POST /api/interview/start

{
  "name": "Ada",
  "role": "Backend Developer",
  "experience": "2 Years",
  "difficulty": "Medium",
  "questionCount": 10
}

difficulty is one of Easy, Medium, Hard. questionCount is a whole number from 1 to 30.

201 returns:

{
  "sessionId": "uuid",
  "welcomeMessage": "Welcome, Ada! ...",
  "firstQuestion": { "id": 1, "text": "..." },
  "totalQuestions": 10
}

POST /api/interview/answer

{ "sessionId": "uuid", "questionId": 1, "question": "(ignored)", "answer": "My answer..." }

The answer is evaluated against the question the server stored, so question is accepted for compatibility but ignored.

200 returns:

{
  "evaluation": {
    "score": 72, "technicalAccuracy": 70, "communication": 78,
    "strengths": ["..."], "weaknesses": ["..."], "feedback": "..."
  },
  "nextQuestion": { "id": 2, "text": "..." },
  "isLastQuestion": false
}

For the last question, nextQuestion is null and isLastQuestion is true.

Status Meaning
404 no such in-progress interview for this user
409 this question was already answered, or is being evaluated right now
429 Groq rate limit; wait a minute and retry
502 the AI provider failed to produce a valid response

POST /api/interview/finish

{ "sessionId": "uuid" }

200 returns data.report:

{
  "candidateName": "Ada", "role": "...", "experience": "...", "difficulty": "Medium",
  "totalQuestions": 10, "questionsAnswered": 10,
  "overallScore": 74, "technicalAccuracy": 71, "communicationScore": 79,
  "strengths": ["..."], "weaknesses": ["..."], "feedback": "...",
  "suggestedTopics": ["..."],
  "questionResults": [{ "questionId": 1, "question": "...", "answer": "...", "score": 72, "...": "..." }]
}

The interview moves into history. Calling finish again returns 404.

GET /api/interview/history

200 returns data.history: completed interviews, newest first. Each entry has the report fields above plus _id (the interview id) and date (when it was completed).

Voice πŸ”’

POST /api/voice/transcribe

multipart/form-data with exactly one file field named audio (webm, ogg, mp4/m4a, mp3, wav or flac, at most 25 MB) and no other fields.

200 returns { "text": "transcribed answer" }.

Status Meaning
400 no audio, an extra field, or more than one file
413 file larger than 25 MB
415 unsupported audio type
429 Groq rate limit

POST /api/voice/speak

{ "text": "Up to 200 characters." }

200 returns audio/wav (24 kHz mono PCM). Any other status tells the client to use the browser voice:

Status Meaning
400 text empty or longer than 200 characters
429 Groq TTS rate limit or daily quota reached
501 server TTS disabled (TTS_PROVIDER=browser)
503 the Orpheus terms haven’t been accepted in the Groq console