Finish sign-in with the code or link
Either { email, code } or { token } from the magic link. A refusal is 401 with a code saying why. Code: OTP_INVALID (wrong code — details.attemptsLeft; an unknown address answers exactly the same), OTP_LOCKED (OTP_MAX_ATTEMPTS wrong codes, default 5 — ask for a new code, which unlocks), OTP_EXPIRED (right code, ran out or replaced by a newer one), OTP_USED (right code, already used). Link: OTP_LINK_INVALID (not a link we sent), OTP_LINK_EXPIRED (ran out or replaced), OTP_LINK_USED — the last two can ask for a new link with POST /auth/otp/resend. Limit: RATE_OTP_VERIFY_PER_IP (default 40 per hour).
application/json- body
email*stringthe email the code was sent to
emaillength <= 254code*stringthe six digits from the email
^\d{6}$Access JWT in the envelope; refresh token in an httpOnly cookie
application/json- response
success*truetruedata*curl -X POST 'http://localhost:3000/auth/otp/verify' \ -H 'Content-Type: application/json' \ -d '{ "email": "user@example.com", "code": "string"}'{ "success": true, "data": { "accessToken": "string", "expiresAtMs": "string" }}Request a sign-in code or link POST
Emails a six-digit code (delivery=code, for typing on mobile) or a magic link (delivery=link, for clicking on desktop). Signs up unknown emails. 202 whether or not the account exists — that is never disclosed; the only refusals are an address that cannot receive mail at all and too many requests. Limits (env): RATE_OTP_REQUEST_PER_EMAIL (default 5 per hour, counted for unknown addresses too) and RATE_OTP_REQUEST_PER_IP (default 20 per hour). A new request also clears the address's wrong-code count.
Mail a new magic link from a dead one POST
The dead link's token says where the new link goes — the caller never names an address. Voids any live code or link and mails a fresh link to the same account. Always 202: `emailMasked` is null when the token is not a link we sent or is older than OTP_RESEND_MAX_AGE_HOURS (default 168), and a disabled account is answered the same and mailed nothing. Limits: RATE_OTP_RESEND_PER_LINK (default 3 per hour), the address's RATE_OTP_REQUEST_PER_EMAIL budget (shared with /auth/otp/request) and RATE_OTP_REQUEST_PER_IP.
