Skip to main content
  • npm
  • Script Tag

Installation

Install the OTPless Headless SDK with your favorite package manager:
npm install otpless-headless-js
# or
yarn add otpless-headless-js
# or
pnpm add otpless-headless-js

🚀 Quick Start (React Example)

import React, { useEffect, useState } from 'react';
import { useOTPless, CHANNELS, EVENT_TYPES } from 'otpless-headless-js';

function ExampleOTPlessAuth() {
  const { init, initiate, verify, on, loading, ready } = useOTPless();
  const [phone, setPhone] = useState('9876543210');
  const [countryCode, setCountryCode] = useState('+91');
  const [otp, setOtp] = useState('');
  const [status, setStatus] = useState('');

  useEffect(() => {
    // Always pass your actual OTPless App ID
    init('YOUR_APP_ID');
    // Subscribe to single and multiple events
    const unsubAutoRead = on(EVENT_TYPES.OTP_AUTO_READ, (event) => {
      const autoOtp = event?.response?.otp ?? '';
      setOtp(autoOtp);
      setStatus(`OTP auto-read: ${autoOtp}`);
    });
    const unsubAll = on({
      [EVENT_TYPES.ONETAP]: (event) => setStatus('OneTap Auth Success!'),
      [EVENT_TYPES.OTP_AUTO_READ]: (event) => {
        const autoOtp = event?.response?.otp ?? '';
        setOtp(autoOtp);
        setStatus(`OTP auto-read: ${autoOtp}`);
      },
      [EVENT_TYPES.FAILED]: (event) => setStatus(`Failed: ${event.response?.errorMessage}`),
    });
    return () => {
      unsubAutoRead();
      unsubAll();
    };
  }, [init, on]);

  const sendOtp = async () => {
    setStatus('');
    if (!ready) {
      setStatus('SDK not ready yet.');
      return;
    }
    try {
      const result = await initiate({ channel: CHANNELS.PHONE, phone, countryCode });
      if (result.success) setStatus('OTP sent! Check your device.');
      else setStatus(`Failed to send OTP: ${result.response?.errorMessage || 'Unknown error'}`);
    } catch (err) {
      setStatus(`Error sending OTP: ${err.message || err}`);
    }
  };

  const verifyOtp = async () => {
    setStatus('');
    if (!ready) {
      setStatus('SDK not ready yet.');
      return;
    }
    try {
      const result = await verify({ channel: CHANNELS.PHONE, phone, countryCode, otp });
      if (result.success) setStatus('OTP verified! Authenticated.');
      else setStatus(`OTP verification failed: ${result.response?.errorMessage || 'Unknown error'}`);
    } catch (err) {
      setStatus(`Verification error: ${err.message || err}`);
    }
  };

  if (!ready) return <div>Loading OTPless SDK...</div>;

  return (
    <div style={{ maxWidth: 400 }}>
      <div>
        <input
          value={countryCode}
          onChange={e => setCountryCode(e.target.value)}
          maxLength={5}
          placeholder="Country Code"
          style={{ width: '80px', marginRight: '8px' }}
        />
        <input
          value={phone}
          onChange={e => setPhone(e.target.value)}
          placeholder="Phone Number"
          style={{ width: '160px' }}
        />
      </div>
      <button onClick={sendOtp} disabled={loading} style={{ marginTop: 10, width: '100%' }}>
        {loading ? 'Sending...' : 'Send OTP'}
      </button>
      <div style={{ marginTop: 12 }}>
        <input
          value={otp}
          onChange={e => setOtp(e.target.value)}
          placeholder="Enter OTP"
          style={{ width: '150px', marginRight: '8px' }}
        />
        <button onClick={verifyOtp} disabled={loading || !otp}>
          {loading ? 'Verifying...' : 'Verify OTP'}
        </button>
      </div>
      {status && (
        <div style={{ marginTop: 12, color: status.includes('Failed') || status.includes('error') ? 'red' : 'green' }}>
          {status}
        </div>
      )}
    </div>
  );
}

🧑‍💻 API Reference

useOTPless()

Returns an object with:
PropertyTypeDescription
readybooleanWhether the OTPless SDK script is loaded and ready.
loadingbooleantrue if any operation (init/initiate/verify) is in progress.
init(appId: string) => Promise<void>Initialize the SDK with your App ID. Call once before use.
initiate(request: InitiateRequest) => Promise<OTPlessResponse>Send OTP (via CHANNELS.PHONE, etc).
verify(request: VerifyRequest) => Promise<OTPlessResponse>Verify the OTP received by the user.
on(see below)Subscribe to events; signature below.
off(event, callback) => voidRemove a previously registered listener.

on(event, handler?)

  • Subscribe to a single event:
    on(EVENT_TYPES.OTP_AUTO_READ, event => { ... })
    
  • Or to multiple events at once:
    on({
      [EVENT_TYPES.ONETAP]: handler1,
      [EVENT_TYPES.FAILED]: handler2,
      // ...
    })
    
  • Returns an unsubscribe function.

off(event, handler)

  • Unsubscribe a specific event handler.

TypeScript Types & Constants

import type { InitiateRequest, VerifyRequest, EventType, OTPlessResponse } from 'otpless-headless-js';
import { CHANNELS, EVENT_TYPES } from 'otpless-headless-js';

const req: InitiateRequest = { channel: CHANNELS.PHONE, phone: '9876543210', countryCode: '+91' };

const onEvent = (type: EventType) => {
  if (type === EVENT_TYPES.FAILED) {
    console.error('Operation failed');
  }
};

OTPless Response Type

All SDK calls (initiate, verify) return:
type OTPlessResponsePayload = {
  otp?: string;
  token?: string;
  [key: string]: any;
};

type OTPlessResponse = {
  responseType: string;
  response?: OTPlessResponsePayload;
  success: boolean;
  statusCode: number;
};
Example:
const res: OTPlessResponse = await initiate({
  channel: CHANNELS.PHONE,
  phone: '9876543210',
  countryCode: '+91',
});
if (res.success) {
  console.log('requestId:', res.response?.requestID);
} else {
  console.error('initiate failed:', res.response?.errorMessage);
}

⚡️ Framework-Agnostic Usage

You can use the SDK without React (in any framework):
import { otpless, CHANNELS, EVENT_TYPES } from 'otpless-headless-js';

// Initialize
await otpless.init('YOUR_APP_ID');

// Send OTP
await otpless.initiate({
  channel: CHANNELS.PHONE,
  phone: '9876543210',
  countryCode: '+91',
});

// Verify OTP
await otpless.verify({
  channel: CHANNELS.PHONE,
  phone: '9876543210',
  countryCode: '+91',
  otp: '123456',
});

// Listen to single/multiple events
const unsub = otpless.on(EVENT_TYPES.OTP_AUTO_READ, event => {
  console.log('OTP auto-read:', event.response?.otp);
});
const unsubAll = otpless.on({
  [EVENT_TYPES.ONETAP]: event => console.log('Auth Success:', event),
  [EVENT_TYPES.FAILED]: event => console.error('Failed:', event.response?.errorMessage),
});

🛠 Utility Functions

import { normalizeCountryCode, digitsOnly, redactTokens } from 'otpless-headless-js';

// Normalize country codes
normalizeCountryCode('91'); // '+91'
normalizeCountryCode('+1-234'); // '+1234'

// Extract digits
digitsOnly('98-765-43210'); // '9876543210'

// Redact tokens for safe logging
const safeData = redactTokens({
  phone: '9876543210',
  otp: '123456',
  token: 'secret'
});
// { phone: '9876543210', otp: '***redacted***', token: '***redacted***' }

🟢 Next.js Usage

For Next.js App Router, add 'use client'; at the top of your component file!
'use client';
import { useOTPless } from 'otpless-headless-js';

export default function AuthPage() {
  const { ready, loading, init, initiate, verify } = useOTPless();

  // ... your component logic here
}

Find npm package here: https://www.npmjs.com/package/otpless-headless-js Find github repository here: https://github.com/otpless-tech/web-headless-demo Find sandbox here: https://codesandbox.io/s/github/otpless-tech/web-headless-demo

🏁 Checkpoint

To ensure a smooth integration process:
  1. Deploy your app/website with the included OTPLESS SDK.
  2. Conduct tests to verify the sign-in flow functions correctly.
  3. Ensure that after a successful sign-in, the user is redirected back to your app/website and their information is correctly logged in the console.

User Information Response Structure

The structure of the user information returned upon successful sign-in is as follows:
{
  "status": "SUCCESS",
  "token": "unique_token_here",
  "userId": "unique_user_id_here",
  "timestamp": "ISO_timestamp_here",
  "identities": [
    {
       "identityType": "EMAIL",
      "identityValue": "user@example.com",
      "channel": "OAUTH",
      "methods": [
        "GOOGLE"
      ],
      "name": "User Name",
      "verified": true,
      "verifiedAt": "ISO_timestamp_here",
      "isCompanyEmail": "true"
    }
  ],
  "idToken": "jwt_token",
  "network": {
    "ip": "127.0.0.1",
    "timezone": "Asia/Kolkata",
    "ipLocation": {}
  },
  "deviceInfo": {},
  "sessionInfo": {},
  "firebaseInfo": {},
}
You can check out a complete sample response here.

Next Steps

I