Candidate Flow

OTP Verification

How candidates verify their identity via a one-time password before joining an interview.

When a candidate opens their interview link, they must verify email ownership before accessing the interview. A 6-digit OTP is sent to the email registered with the interview request, confirming the candidate is who they claim to be.

Verification Steps

StepActionAPI Endpoint
1Page loads — fetch interview info to display name and positionGET /candidate-access/:token/info
2Candidate clicks Send Code — OTP emailed to masked addressPOST /candidate-access/:token/request-otp
3Candidate enters 6 digits — receives session token on successPOST /candidate-access/:token/verify-otp

Page: /interview/join/:token

The join page renders in three states as the candidate progresses:

UI StateWhat Candidate Sees
LoadingSpinner while fetching interview info
Info loadedName, position, masked email (j***@e***.com), Send Code button
Code sent6-digit input, resend countdown, Verify button
VerifiedRedirect to /interview/pre-check/:token
text
/interview/join/abc123token
  │
  ├── GET /candidate-access/abc123token/info
  │     └── Shows: "Welcome, Jane Smith — Senior Engineer"
  │
  ├── POST /candidate-access/abc123token/request-otp
  │     └── "Verification code sent to j***@e***.com"
  │
  ├── POST /candidate-access/abc123token/verify-otp { otp: "123456" }
  │     └── sessionToken stored in sessionStorage
  │
  └── Navigate → /interview/pre-check/abc123token

OTP Constraints

PropertyValue
Length6 digits
Validity10 minutes from generation
Rate limit5 requests per 10 minutes per token
Attempts3 invalid attempts triggers cooldown
ResendAvailable after 60 second countdown

Session Token

On successful OTP verification, the frontend stores these values in sessionStorage:

typescript
// Frontend stores after successful OTP verify
sessionStorage.setItem('interview_session_token', data.sessionToken);
sessionStorage.setItem('interview_token', token); // URL token
sessionStorage.setItem('interview_candidate_name', data.candidateName);
sessionStorage.setItem('interview_position', data.position);

The sessionToken is a JWT valid for the duration of the interview. It is passed as the x-session-token header in the subsequent start call.

Error Handling

ErrorCauseUI Response
404Token expired or invalidFull-page error: link expired message
401Invalid OTP codeInline error: incorrect code, n attempts remaining
429Rate limit exceededShow cooldown timer, disable send button
The masked email (j***@e***.com) is shown before sending the OTP to help the candidate confirm they are at the right interview link without exposing the full email address.
Was this page helpful?