Email Module
The Email module verifies a user's email address via OTP (One-Time Password).
This guide is specific to Web SDK 2.0. If you are still using 1.x, you can find documentation here. We strongly recommend upgrading - contact your Incode Representative for upgrade information.
The Email module verifies a user's email address via OTP (One-Time Password).
Follows the form-based pattern (with the OTP sub-loop). See that page for the shared manager lifecycle and skeleton; the rest of this page covers Email-specific config, states, and methods.
Tag
<incode-email> is a standard Web Component. Importing the UI subpath registers the custom element; importing the CSS applies the module's styles.
import '@incodetech/web/email';
import '@incodetech/web/email/styles.css';Properties
Set these as JavaScript properties on the element (not as HTML attributes):
| Property | Type | Required | Description |
|---|---|---|---|
config | EmailConfig | ✅ | Configuration options |
onFinish | () => void | ❌ | Called when verification completes |
onError | (error: string) => void | ❌ | Called when an error occurs |
Usage
Vanilla HTML / TypeScript
<incode-email></incode-email>
<script type="module">
import { setup } from '@incodetech/core';
import '@incodetech/web/email';
import '@incodetech/web/email/styles.css';
await setup({
apiURL: 'https://demo-api.incodesmile.com',
token: 'your-session-token',
});
const email = document.querySelector('incode-email');
email.config = {
otpVerification: true,
otpExpirationInMinutes: 5,
prefill: false,
};
email.onFinish = () => console.log('Email verified!');
email.onError = (err) => console.error('Email error:', err);
</script>React
React 18 or earlier: add the one-time JSX augmentation from Framework Integration → TypeScript: JSX support for
incode-*tags. React 19+ doesn't need it, and can also use the simpler form from Framework Integration → React 19+ shortcut.
import { useEffect, useRef } from 'react';
import { setup } from '@incodetech/core';
import type { EmailConfig } from '@incodetech/core/email';
import '@incodetech/web/email';
import '@incodetech/web/email/styles.css';
type EmailElement = HTMLElement & {
config: EmailConfig;
onFinish: () => void;
onError: (error: string) => void;
};
await setup({
apiURL: 'https://demo-api.incodesmile.com',
token: 'your-session-token',
});
export function EmailVerification() {
const ref = useRef<EmailElement>(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
el.config = {
otpVerification: true,
otpExpirationInMinutes: 5,
prefill: false,
};
el.onFinish = () => console.log('Email verified!');
el.onError = (err) => console.error('Email error:', err);
}, []);
return <incode-email ref={ref} />;
}For Angular (CUSTOM_ELEMENTS_SCHEMA) and Vue (compilerOptions.isCustomElement) setup, see Framework Integration.
Headless Mode
For complete UI control, use the createEmailManager from @incodetech/core/email.
Quick Start
import { setup } from '@incodetech/core';
import { createEmailManager } from '@incodetech/core/email';
// Initialize SDK first
await setup({
apiURL: 'https://demo-api.incodesmile.com',
token: 'your-session-token',
});
const manager = createEmailManager({
config: {
otpVerification: true,
otpExpirationInMinutes: 5,
prefill: false,
},
});
manager.subscribe((state) => {
console.log('Status:', state.status);
if (state.status === 'finished') {
console.log('Email verified!');
manager.stop();
}
});
// Start the flow
manager.load();
// When state is 'inputting', set the email and submit
manager.setEmail('[email protected]', true);
manager.submit();
// When state is 'awaitingOtp', submit the OTP code
manager.submitOtp('ABC123');State Machine Flow
flowchart LR
idle -->|load| inputting
inputting -->|submit| awaitingOtp
awaitingOtp -->|submitOtp| finished
awaitingOtp -.->|back| inputting
awaitingOtp -.->|resendOtp| awaitingOtp
States Reference
| Status | Description | Key Properties |
|---|---|---|
idle | Initial state, waiting for load() | – |
loadingPrefill | Fetching pre-filled email from backend | – |
inputting | Ready for email input | prefilledEmail?, emailError?, otpVerification? |
submitting | Submitting email to backend | – |
sendingInitialOtp | Sending OTP code via email for the first time | – |
resendingOtp | Resending OTP code via email | – |
awaitingOtp | Waiting for user to enter OTP | resendTimer, canResend, attemptsRemaining |
verifyingOtp | Verifying OTP code with backend | resendTimer, canResend |
otpError | OTP verification failed | otpError, attemptsRemaining, resendTimer, canResend |
finished | Verification complete | – |
error | Fatal error occurred | error |
State Properties
When status === 'inputting':
| Property | Type | Description |
|---|---|---|
prefilledEmail | string? | Pre-populated email address (if prefill: true) |
emailError | string? | Validation error from server |
When status === 'awaitingOtp':
| Property | Type | Description |
|---|---|---|
resendTimer | number | Seconds remaining before resend allowed |
canResend | boolean | Whether resend button should be enabled |
attemptsRemaining | number | OTP verification attempts left |
When status === 'otpError':
| Property | Type | Description |
|---|---|---|
otpError | string | Error message describing failure |
attemptsRemaining | number | Remaining attempts before lockout |
resendTimer | number | Seconds remaining before resend allowed |
canResend | boolean | Whether resend button should be enabled |
API Methods
| Method | Description | When to Use |
|---|---|---|
load() | Initializes the flow | Always call first |
setEmail(email, isValid) | Sets email address and validation state | When inputting, before submit() |
submit() | Submits the email address | After setting valid email |
setOtpCode(code) | Sets OTP without submitting | When awaitingOtp (controlled input) |
submitOtp(code) | Sets and submits OTP code | When awaitingOtp or otpError |
resendOtp() | Requests new OTP code | When canResend is true |
back() | Returns to email input | When awaitingOtp |
reset() | Resets to initial state | After finished or error |
stop() | Cleanup resources | When unmounting |
getState() | Returns current state synchronously | Anytime |
subscribe(callback) | Subscribe to state changes | Returns unsubscribe function |
React Example
import { useState, useEffect } from 'react';
import { createEmailManager, type EmailState } from '@incodetech/core/email';
function CustomEmailVerification() {
const [manager] = useState(() => createEmailManager({
config: { otpVerification: true, otpExpirationInMinutes: 5, prefill: false },
}));
const [state, setState] = useState<EmailState>({ status: 'idle' });
const [email, setEmail] = useState('');
const [otp, setOtp] = useState('');
useEffect(() => {
const unsubscribe = manager.subscribe(setState);
manager.load();
return () => { unsubscribe(); manager.stop(); };
}, [manager]);
const handleSubmitEmail = () => {
// Simple email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const isValid = emailRegex.test(email);
manager.setEmail(email, isValid);
manager.submit();
};
switch (state.status) {
case 'inputting':
return (
<div>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter email address"
/>
{state.emailError && <p className="error">{state.emailError}</p>}
<button onClick={handleSubmitEmail}>Send OTP</button>
</div>
);
case 'submitting':
case 'sendingInitialOtp':
case 'resendingOtp':
return <div>Sending OTP...</div>;
case 'awaitingOtp':
return (
<div>
<p>Enter the 6-character code sent to your email</p>
<input
type="text"
value={otp}
onChange={(e) => setOtp(e.target.value.toUpperCase())}
placeholder="ABC123"
maxLength={6}
/>
<button onClick={() => manager.submitOtp(otp)}>Verify</button>
{state.canResend ? (
<button onClick={() => manager.resendOtp()}>Resend Code</button>
) : (
<p>Resend available in {state.resendTimer}s</p>
)}
<button onClick={() => manager.back()}>Change Email</button>
</div>
);
case 'otpError':
return (
<div>
<p className="error">{state.otpError}</p>
<p>Attempts remaining: {state.attemptsRemaining}</p>
<input
type="text"
value={otp}
onChange={(e) => setOtp(e.target.value.toUpperCase())}
maxLength={6}
/>
<button onClick={() => manager.submitOtp(otp)}>Try Again</button>
</div>
);
case 'verifyingOtp':
return <div>Verifying...</div>;
case 'finished':
return <div>Email verified successfully!</div>;
case 'error':
return <div className="error">Error: {state.error}</div>;
default:
return <div>Loading...</div>;
}
}Configuration Options
EmailConfig shape:
| Option | Type | Required | Description |
|---|---|---|---|
otpVerification | boolean | ✅ | Require OTP (email code) verification. If false, the email is verified immediately after submission. |
otpExpirationInMinutes | number | ✅ | How long the OTP code remains valid. After expiration the user must request a new code. |
prefill | boolean | ❌ | Pre-populate with the user's previously stored email. Useful for returning users. Default false. |
maxOtpAttempts | number | ❌ | Maximum OTP verification attempts before lockout. Default 3. |
Troubleshooting
OTP Not Received
- Check the spam/junk folder
- Verify the email address is correct
- Some corporate email filters may block OTP emails
- Add the sender to your contacts/whitelist
Invalid OTP Error
- OTPs expire after the configured time (default 5 minutes)
- Each OTP can only be used once
- Request a new OTP if the current one expires
- Check
attemptsRemainingbefore lockout
Email Address Rejected
- Ensure the email format is valid
- Some disposable email domains may be blocked
- Corporate email addresses are generally accepted
See Also
- Headless Mode: Complete headless API reference
- Individual Modules: Overview of all modules
Updated 1 day ago
