threshold1

Email OTP

A 6-digit code sent by email. This is the method the Quickstart uses as its first working example — it needs nothing beyond your API key, no dashboard configuration of any kind.

Setup

None. sendOtp()/verifyOtp() work immediately with just apiKey in the constructor. If you haven't been through it yet, the Quickstart walks the full real flow end to end.

Sending and verifying a code

await auth.sendOtp("user@example.com");
// Optionally thread your own user ID through, same identity-bridge rules
// as everywhere else:
await auth.sendOtp("user@example.com", "your_user_123");

// Once the user enters the code from their email:
const user = await auth.verifyOtp("user@example.com", "123456");
// { id, email, externalUserId }

A successful verifyOtp() authenticates the SDK instance and fires onAfterAuth, same as any other primary login path.

Real configuration values

  • Code expiry: 5 minutes.
  • Code format: 6 digits, numeric, zero-padded (000000999999).
  • Lockout: 5 attempts. After 5 wrong guesses against a given code, that code is locked out — the user has to request a new one. Getting the code right on the 6th attempt doesn't work even if it's correct.
  • Send rate limit: 5 sends per email address per 10 minutes.

None of these are currently configurable per project.

Failure codes

sendOtp() can be rate-limited on repeated sends. verifyOtp() can return invalid_code, expired, or rate_limited (the 5-attempt lockout). See the Error Reference for what to show users and how to recover.

What's next

  • External User ID Bridging — how the optional externalUserId argument resolves to a user.
  • Environments — OTP codes are scoped per test/live environment like everything else.