Phone Module

Web SDK 2.0 Phone Module

This guide is specific to Web SDK 2.0. If you are still using 1.x, you can find documentation here. Contact your Incode Representative for upgrade information and check if you are a candidate for this upgrade.

Full rollout to all clients still TBD.

The Phone module verifies a user's phone number via SMS OTP (One-Time Password).

Tag

<incode-phone> is a standard Web Component. Importing the UI subpath registers the custom element; importing the CSS applies the module's styles.

Code Snippet

import '@incodetech/web/phone';
import '@incodetech/web/phone/styles.css';

Properties

Set these as JavaScript properties on the element (not as HTML attributes):

Property Type Required Description
config PhoneConfig Configuration options
onFinish () => void Called when verification completes
onError (error: string) => void Called when an error occurs

Usage

Vanilla HTML / TypeScript

Code Snippet

<incode-phone></incode-phone>

<script type="module">
  import { setup } from '@incodetech/core';
  import '@incodetech/web/phone';
  import '@incodetech/web/phone/styles.css';

await setup({
    apiURL: 'https://demo-api.incodesmile.com',
    token: 'your-session-token',
  });

const phone = document.querySelector('incode-phone');
  phone.config = {
    otpVerification: true,
    otpExpirationInMinutes: 5,
    prefill: false,
  };
  phone.onFinish = () => console.log('Phone verified!');
  phone.onError = (err) => console.error('Phone error:', err);
</script>

React

Code Snippet

import { useEffect, useRef } from 'react';
import { setup } from '@incodetech/core';
import type { PhoneConfig } from '@incodetech/core/phone';
import '@incodetech/web/phone';
import '@incodetech/web/phone/styles.css';

type PhoneElement = HTMLElement & {
  config: PhoneConfig;
  onFinish: () => void;
  onError: (error: string) => void;
};

await setup({
  apiURL: 'https://demo-api.incodesmile.com',
  token: 'your-session-token',
});

export function PhoneVerification() {
  const ref = useRef<PhoneElement>(null);

useEffect(() => {
    const el = ref.current;
    if (!el) return;
    el.config = {
      otpVerification: true,
      otpExpirationInMinutes: 5,
      prefill: false,
    };
    el.onFinish = () => console.log('Phone verified!');
    el.onError = (err) => console.error('Phone error:', err);
  }, []);

return <incode-phone ref={ref} />;
}

Headless Mode

For complete UI control, use the createPhoneManager from @incodetech/core/phone.

Quick Start

Code Snippet

import { setup } from '@incodetech/core';
import { createPhoneManager } from '@incodetech/core/phone';

// Initialize SDK first
await setup({
  apiURL: 'https://demo-api.incodesmile.com',
  token: 'your-session-token',
});

const manager = createPhoneManager({
  config: {
    otpVerification: true,
    otpExpirationInMinutes: 5,
    prefill: false,
  },
});

manager.subscribe((state) => {
  console.log('Status:', state.status);

if (state.status === 'finished') {
    console.log('Phone verified!');
    manager.stop();
  }
});

// Start the flow
manager.load();

// When state is 'inputting', set the phone number and submit
manager.setPhoneNumber('+14155551234', true);
manager.submit();

// When state is 'awaitingOtp', submit the OTP code
manager.submitOtp('ABC123');

State Machine Flow

load

submit

submitOtp

back

resendOtp

idle

inputting

awaitingOtp

finished

States Reference

Status Description Key Properties
idle Initial state, waiting for load()
loadingPrefill Fetching pre-filled phone from backend
loadingStartInfo Fetching start info (ipIsoCode, phonePrefix) before the field is interactive prefilledPhone?, otpVerification?, optinEnabled?
inputting Ready for phone input countryCode, phonePrefix, prefilledPhone?, phoneError?, validationErrors?, isValid, otpVerification?, optinEnabled?
submitting Submitting phone number to backend
sendingInitialOtp Sending OTP code via SMS for the first time
resendingOtp Resending OTP code via SMS otpCode, otpLength
awaitingOtp Waiting for user to enter OTP resendTimer, canResend, attemptsRemaining, otpCode, otpLength, otpValidationErrors?, isOtpValid
verifyingOtp Verifying OTP code with backend resendTimer, canResend, otpCode, otpLength
otpError OTP verification failed otpError, attemptsRemaining, resendTimer, canResend, otpCode, otpLength, otpValidationErrors?, isOtpValid
finished Verification complete
error Fatal error occurred error

API Methods

Method Description When to Use
load() Initializes the flow Always call first
setPhoneNumber(phone, isValid) Sets phone number and validation state When inputting, before submit()
submit() Submits the phone number After setting valid phone
submitOtp(code) Sets and submits OTP code When awaitingOtp or otpError
resendOtp() Requests new OTP code When canResend is true
back() Returns to phone 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

Code Snippet

import { useState, useEffect } from 'react';
import { createPhoneManager, type PhoneState } from '@incodetech/core/phone';

function CustomPhoneVerification() {
  const [manager] = useState(() =>
    createPhoneManager({
      config: {
        otpVerification: true,
        otpExpirationInMinutes: 5,
        prefill: false,
      },
    }),
  );
  const [state, setState] = useState<PhoneState>({ status: 'idle' });
  const [phone, setPhone] = useState('');
  const [otp, setOtp] = useState('');

useEffect(() => {
    const unsubscribe = manager.subscribe(setState);
    manager.load();
    return () => {
      unsubscribe();
      manager.stop();
    };
  }, [manager]);

const handleSubmitPhone = () => {
    const isValid = phone.length >= 10;
    manager.setPhoneNumber(phone, isValid);
    manager.submit();
  };

switch (state.status) {
    case 'inputting':
      return (
        <div>
          <p>
            Country: {state.countryCode} ({state.phonePrefix})
          </p>
          <input
            type="tel"
            value={phone}
            onChange={(e) => setPhone(e.target.value)}
            placeholder="Enter phone number"
          />
          {state.phoneError && <p className="error">{state.phoneError}</p>}
          <button onClick={handleSubmitPhone}>Send OTP</button>
        </div>
      );

case 'awaitingOtp':
      return (
        <div>
          <p>Enter the 6-character code sent to your phone</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>
          )}
        </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 'finished':
      return <div>Phone verified successfully!</div>;

case 'error':
      return <div className="error">Error: {state.error}</div>;

default:
      return <div>Loading...</div>;
  }
}

Configuration Options

PhoneConfig shape:

Option Type Required Description
otpVerification boolean Require OTP (SMS code) verification. If false, the phone 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 phone number. Useful for returning users.
isInstantVerify boolean Use carrier-based instant verification instead of OTP. Not available in all regions. Default false.
optinEnabled boolean Show a marketing opt-in checkbox. The user's preference is sent with the phone submission. Default false.
maxOtpAttempts number Maximum OTP verification attempts before lockout. Default 3.

Troubleshooting

OTP Not Received

Invalid OTP Error

Phone Number Rejected