Skip to content

Email MFA

Account verification sends a one-time code by email to an address you name, and the person types it into the widget. Use it when an assessment says an account looks risky (a login from a new device with a low score) and you want proof the owner is present.

It follows the shape of reCAPTCHA Enterprise’s accountVerification, is part of the Pro plan and above, and needs the site’s mfa policy (enabled, scoreBelow, labels, optional senderName) to be set in the dashboard.

  1. Your server creates an assessment with accountVerification and userInfo naming the account.
  2. If the answer has requiresMfa: true, accountVerification.endpoints[0].requestToken is the session handle.
  3. The client calls challengeAccount(sitekey, { requestToken }) (web wrapper) or challengeAccount(requestToken:) (mobile SDKs). The widget calls POST /v1/mfa/send and POST /v1/mfa/verify, authenticated by that requestToken alone.
  4. The client gets a verification token. Your server presents it to a new assessment of the same account as event.token. The assessment then says SUCCESS_USER_VERIFIED or ERROR_USER_NOT_VERIFIED.
const a = await rg.createAssessment({
token,
userInfo: { accountId: user.id },
accountVerification: { endpoints: [{ emailAddress: user.email }], languageCode: 'en' },
});
if (a.requiresMfa) {
const [first] = a.accountVerification?.endpoints ?? [];
// hand first.requestToken to the client's challengeAccount
}

Later, with the token the client returns:

const b = await rg.createAssessment({
token: verificationToken,
userInfo: { accountId: user.id },
});
// b.accountVerification.latestVerificationResult === 'SUCCESS_USER_VERIFIED'

/v1/mfa/send answers { "sent": true, "resendAfter": 60 }, or { "result": "..." } when the send cannot happen (the session is then over). /v1/mfa/verify answers { "retry": true } after a wrong code with attempts left, or { "token": "..." } as the final answer.

  • A session lives 600 seconds. A code has six digits.
  • At most 3 sends per session, 60 seconds apart. 5 wrong codes in total, then the session ends.
  • The final token is given once per session and is single use.
  • Request bodies are limited to 1 KiB.
Status and code Meaning What to do
403 feature-disabled Account verification is not on for the site (or a phoneNumber endpoint was sent). Enable it, or use email.
404 not-found The requestToken is forged, malformed or expired, or the site is unknown. Start again with a new assessment.
429 rate-limited Too fast. Wait.
503 service-unavailable Account verification is switched off or unavailable. Fall back to your own step-up.

If the latest result is ERROR_RECIPIENT_ABUSE_LIMIT_EXHAUSTED (too many codes to that mailbox, possibly from others), offer another verification path. Never lock the person out.

  • We send no SMS. A phoneNumber endpoint passes the schema and is answered 403 feature-disabled; nothing is sent.
  • The email address comes from your own records, never from the request being checked.
  • Treat requestToken as a secret of the session: pass it to that user’s client only.