Integration Guide
Developer HubZero-Dependency REST.
Authepy is engineered as a stateless, headless utility. We handle cryptographic hashing, VIP transit lanes, and IP abuse routing via a pure REST API—keeping your `node_modules` clean and leaving session management entirely in your control.
curl -X POST https://api.authepy.com/api/otp/request \
-H "Authorization: Bearer ath_sec_live_..." \
-H "Content-Type: application/json" \
-d '{"email": "user@startup.com"}' Select your environment
8 Architectures IndexedNode.js Backends
Direct REST integration for custom monolithic architectures. Handle raw rate-limiting and session cookies securely with standard secret keys.
Next.js Framework
Execute verification entirely on the server. Integrate directly with App Router Server Actions or wrap into Auth.js (NextAuth) Credentials provider.
React Engine (SPA)
Build 100% custom UI states without forcing a frontend SDK into your bundle. Verify safely in browser using domain-whitelisted API keys.
Mobile Apps
Secure React Native and Flutter implementations. Use the Backend Proxy Pattern to prevent API keys from being exposed in compiled binaries.
Web3 & DApps
Cryptographically bind off-chain emails to on-chain wallet signatures (SIWE) instantly. Keep your verification logic stateless and decentralized.
WordPress Core
Eliminate brute force attacks. Replace standard WordPress passwords with Authepy OTPs using native wp_remote_post functions and secure shortcodes.
Site Builders
Easily add secure Email Verification (OTP) to your Wix, Webflow, or Framer site without writing backend code. Protect from fake signups.
Zapier & Automations
Add secure Email Verification to Zapier, Make, and Bubble workflows. Stop fake emails from triggering your expensive automations.
Python Ecosystem
Drop identity verification into your machine learning pipelines, FastAPI edge servers, or Django monoliths using the `requests` library.
Node.js Backends: Bulletproof identity for APIs.
Build completely decoupled architectures. Integrate Authepy's headless OTP infrastructure directly into your Express.js, Python, or Go APIs using a single Standard Secret Key.
Architecture Pipeline
Your frontend client will communicate with your own Express backend. Your Express backend then acts as a secure proxy, attaching your Standard Secret Key to communicate with the Authepy Edge Router.
const express = require('express');
const app = express();
app.use(express.json());
const AUTHEPY_API = 'https://api.authepy.com/api';
// 1. Request an OTP
app.post('/api/auth/send-code', async (req, res) => {
const { email } = req.body;
try {
const authepyRes = await fetch(`${AUTHEPY_API}/otp/request`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.AUTHEPY_SECRET_KEY}`
},
body: JSON.stringify({ email })
});
const data = await authepyRes.json();
if (!authepyRes.ok) return res.status(400).json({ error: data.error });
return res.status(200).json({ success: true, requestId: data.requestId });
} catch (error) {
return res.status(500).json({ error: "Internal server error." });
}
});
// 2. Verify the OTP
app.post('/api/auth/verify-code', async (req, res) => {
const { requestId, userGuess } = req.body;
try {
const authepyRes = await fetch(`${AUTHEPY_API}/otp/verify`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.AUTHEPY_SECRET_KEY}`
},
body: JSON.stringify({ requestId, userGuess })
});
const data = await authepyRes.json();
if (!authepyRes.ok) return res.status(400).json({ error: data.error });
return res.status(200).json({ success: true, message: "User verified!" });
} catch (error) {
return res.status(500).json({ error: "Internal server error." });
}
});
app.listen(3000, () => console.log('Auth server running')); Zero-trust auth for the Edge.
Drop Authepy directly into your Next.js Server Actions. Execute cryptographic OTP verification entirely on Vercel or Cloudflare's Edge network—keeping your client bundle at exactly 0kB.
import { cookies } from 'next/headers';
const AUTHEPY_API = 'https://api.authepy.com/api';
// 1. Dispatch the OTP
export async function sendOTP(prevState: any, formData: FormData) {
const email = formData.get('email');
const res = await fetch(`${AUTHEPY_API}/otp/request`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.AUTHEPY_SECRET_KEY}`
},
body: JSON.stringify({ email })
});
const data = await res.json();
if (data.success) return { step: 'verify', email, requestId: data.requestId };
return { error: data.error };
}
// 2. Verify the User's Input
export async function verifyOTP(prevState: any, formData: FormData) {
const requestId = formData.get('requestId');
const userGuess = formData.get('code');
const res = await fetch(`${AUTHEPY_API}/otp/verify`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.AUTHEPY_SECRET_KEY}`
},
body: JSON.stringify({ requestId, userGuess })
});
const data = await res.json();
if (data.success) {
const cookieStore = await cookies();
cookieStore.set('auth_session', 'secure-jwt', { httpOnly: true, secure: true });
return { success: true };
}
return { error: data.error || "Invalid verification code", step: 'verify', requestId };
} import { useActionState } from 'react';
import { sendOTP, verifyOTP } from '../actions/auth';
export default function Login() {
const [state, action, isPending] = useActionState(sendOTP, null);
const [verifyState, verifyAction, isVerifyPending] = useActionState(verifyOTP, null);
if (verifyState?.success) {
return <h2>Authentication Successful! Redirecting...</h2>;
}
if (state?.step === 'verify' || verifyState?.step === 'verify') {
return (
<form action={verifyAction} className="flex flex-col gap-4 max-w-sm">
<input type="hidden" name="requestId" value={state?.requestId || verifyState?.requestId || ''} />
<input name="code" placeholder="000000" maxLength={6} required />
<button type="submit" disabled={isVerifyPending}>
{isVerifyPending ? 'Verifying...' : 'Verify Code'}
</button>
{verifyState?.error && <p style={{ color: 'red' }}>{verifyState.error}</p>}
</form>
);
}
return (
<form action={action} className="flex flex-col gap-4 max-w-sm">
<input name="email" type="email" placeholder="name@company.com" required />
<button disabled={isPending}>{isPending ? 'Sending OTP...' : 'Login'}</button>
{state?.error && <p style={{ color: 'red' }}>{state.error}</p>}
</form>
);
} Headless identity for React SPAs.
Build 100% custom UI states securely in the browser. Using Authepy's Restricted Keys, you can verify emails directly from your React frontend—no backend proxy required.
Restricted Origin Shielding
The most critical rule of frontend development is that you cannot hide secrets in the browser. If you put a Standard Key in your React code, malicious actors may steal it.
Authepy solves this with Restricted Keys (rk_live_...). These keys are cryptographically bound to your specific domain names. Even if stolen, our Edge Router instantly rejects any request that doesn't originate from your whitelisted website.
Add autoComplete="one-time-code" to input fields to trigger native keyboard suggestions.
import React, { useState } from 'react';
const AUTHEPY_API = 'https://api.authepy.com/api';
const RESTRICTED_API_KEY = 'YOUR_RESTRICTED_KEY_HERE';
export default function SPALogin() {
const [email, setEmail] = useState('');
const [code, setCode] = useState('');
const [requestId, setRequestId] = useState(null);
const [status, setStatus] = useState('idle');
const handleSendOTP = async (e) => {
e.preventDefault();
setStatus('loading');
const response = await fetch(`${AUTHEPY_API}/otp/request`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${RESTRICTED_API_KEY}`
},
body: JSON.stringify({ email })
});
const data = await response.json();
if (data.success) {
setRequestId(data.requestId);
setStatus('verify');
}
};
const handleVerifyOTP = async (e) => {
e.preventDefault();
setStatus('loading');
const response = await fetch(`${AUTHEPY_API}/otp/verify`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${RESTRICTED_API_KEY}`
},
body: JSON.stringify({ requestId, userGuess: code })
});
const data = await response.json();
if (data.success) setStatus('success');
};
if (status === 'success') return <div>Logged in!</div>;
if (status === 'verify') return ( /* OTP Code Input View */ );
return ( /* Email Input Initial Form View */ );
} Secure mobile identity without exposed keys.
Mobile apps must never bundle secret API keys within their source code, as binary files (.apk or .ipa) are easily decompiled. Utilize the Backend Proxy Pattern to keep your application unhackable.
const express = require('express');
const app = express();
app.use(express.json());
const AUTHEPY_API = 'https://api.authepy.com/api';
app.post('/api/mobile/send-otp', async (req, res) => {
const { email } = req.body;
try {
const authRes = await fetch(`${AUTHEPY_API}/otp/request`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.AUTHEPY_SECRET_KEY}`
},
body: JSON.stringify({ email })
});
const data = await authRes.json();
if (!data.success) return res.status(400).json({ error: data.error });
return res.status(200).json({ success: true, requestId: data.requestId });
} catch (error) {
return res.status(500).json({ error: "Internal dispatch error." });
}
});
app.post('/api/mobile/verify-otp', async (req, res) => {
const { requestId, code } = req.body;
try {
const authRes = await fetch(`${AUTHEPY_API}/otp/verify`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.AUTHEPY_SECRET_KEY}`
},
body: JSON.stringify({ requestId, userGuess: code })
});
const data = await authRes.json();
if (!data.success) return res.status(400).json({ error: data.error });
const mobileSessionToken = "generate_your_jwt_here";
return res.status(200).json({ success: true, token: mobileSessionToken });
} catch (error) {
return res.status(500).json({ error: "Internal verification error." });
}
}); import React, { useState } from 'react';
import { View, TextInput, Button, Text, Alert } from 'react-native';
import * as Keychain from 'react-native-keychain';
export default function MobileLoginScreen() {
const [email, setEmail] = useState('');
const [code, setCode] = useState('');
const [requestId, setRequestId] = useState(null);
const [step, setStep] = useState('request');
const YOUR_PROXY_URL = 'https://api.yourbackend.com/mobile';
const handleRequestOTP = async () => {
try {
const res = await fetch(`${YOUR_PROXY_URL}/send-otp`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});
const data = await res.json();
if (data.success) {
setRequestId(data.requestId);
setStep('verify');
} else {
Alert.alert("Error", data.error);
}
} catch (err) { Alert.alert("Error", "Could not reach server."); }
};
const handleVerifyOTP = async () => {
try {
const res = await fetch(`${YOUR_PROXY_URL}/verify-otp`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ requestId, code })
});
const data = await res.json();
if (data.success && data.token) {
await Keychain.setGenericPassword('session', data.token);
Alert.alert("Success", "Logged in securely!");
} else {
Alert.alert("Invalid Code", data.error);
}
} catch (err) { Alert.alert("Error", "Verification failed."); }
};
return (
<View style={{ padding: 20 }}>
{step === 'request' ? (
<>
<TextInput placeholder="name@company.com" value={email} onChangeText={setEmail} keyboardType="email-address" autoCapitalize="none" />
<Button title="Send Login Code" onPress={handleRequestOTP} />
</>
) : (
<>
<Text>Enter the 6-digit code</Text>
<TextInput placeholder="000000" value={code} onChangeText={setCode} keyboardType="number-pad" maxLength={6} textContentType="oneTimeCode" />
<Button title="Verify Code" onPress={handleVerifyOTP} />
</>
)}
</View>
);
} Stateless Web2.5 Bridging.
Don't build centralized honeypots. Authepy allows DApps to verify Web2 emails completely off-chain, cryptographically binding the volatile result to a SIWE (Sign-In with Ethereum) payload in your own database using zero-dependency REST.
import { SiweMessage } from 'siwe';
export async function verifyWallet(req, res) {
const { message, signature } = req.body;
try {
const siweMessage = new SiweMessage(message);
const fields = await siweMessage.verify({ signature });
req.session.address = fields.data.address;
req.session.siwe_verified = true;
return res.json({ ok: true, address: fields.data.address });
} catch (error) {
return res.status(401).json({ error: "Invalid EIP-4361 signature." });
}
} import db from '@/lib/database';
export async function bindIdentity(req, res) {
const { email, requestId, code } = req.body;
const walletAddress = req.session.address;
if (!req.session.siwe_verified) {
return res.status(401).json({ error: "Wallet not authenticated." });
}
const response = await fetch('https://api.authepy.com/api/otp/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.AUTHEPY_SECRET_KEY}`
},
body: JSON.stringify({ requestId, userGuess: code })
});
const otpCheck = await response.json();
if (!otpCheck.success) {
return res.status(400).json({ error: otpCheck.error || "Invalid OTP token." });
}
await db.users.upsert({
where: { address: walletAddress },
update: { email: email, is_verified: true },
create: { address: walletAddress, email: email, is_verified: true }
});
return res.json({ ok: true, message: "Identity bound." });
} Eliminate brute-force attacks on WordPress.
By replacing standard WordPress passwords with Authepy OTPs, you instantly neutralize brute-force attacks and credential stuffing bots using native `wp_remote_post` functions.
<?php
$authepy_api_url = 'https://api.authepy.com/api';
// --- REQUEST OTP ---
add_action('wp_ajax_nopriv_authepy_request', 'authepy_request_otp');
add_action('wp_ajax_authepy_request', 'authepy_request_otp');
function authepy_request_otp() {
global $authepy_api_url;
$email = sanitize_email($_POST['email']);
$response = wp_remote_post($authepy_api_url . '/otp/request', array(
'headers' => array(
'Authorization' => 'Bearer ' . AUTHEPY_SECRET_KEY,
'Content-Type' => 'application/json',
),
'body' => wp_json_encode(array('email' => $email)),
));
$body = json_decode(wp_remote_retrieve_body($response), true);
if ( isset($body['success']) && $body['success'] === true ) {
wp_send_json_success(array('requestId' => $body['requestId']));
} else {
wp_send_json_error(array('message' => $body['error']));
}
}
// --- VERIFY OTP & LOGIN ---
add_action('wp_ajax_nopriv_authepy_verify', 'authepy_verify_otp');
add_action('wp_ajax_authepy_verify', 'authepy_verify_otp');
function authepy_verify_otp() {
global $authepy_api_url;
$request_id = sanitize_text_field($_POST['requestId']);
$user_guess = sanitize_text_field($_POST['code']);
$email = sanitize_email($_POST['email']);
$response = wp_remote_post($authepy_api_url . '/otp/verify', array(
'headers' => array(
'Authorization' => 'Bearer ' . AUTHEPY_SECRET_KEY,
'Content-Type' => 'application/json',
),
'body' => wp_json_encode(array('requestId' => $request_id, 'userGuess' => $user_guess)),
));
$body = json_decode(wp_remote_retrieve_body($response), true);
if ( isset($body['success']) && $body['success'] === true ) {
$user = get_user_by('email', $email);
if ( !$user ) {
$random_password = wp_generate_password(12, false);
$user_id = wp_create_user($email, $random_password, $email);
$user = get_user_by('id', $user_id);
}
wp_set_current_user($user->ID);
wp_set_auth_cookie($user->ID);
wp_send_json_success(array('message' => 'Logged in successfully!'));
} else {
wp_send_json_error(array('message' => $body['error']));
}
}
?> <?php
add_shortcode('authepy_login', 'render_authepy_login_form');
function render_authepy_login_form() {
ob_start(); ?>
<div id="authepy-wp-container">
<input type="email" id="ath-email" placeholder="name@company.com" />
<button onclick="requestAuthepyOTP()">Send Login Code</button>
</div>
<script>
let globalRequestId = null;
let globalEmail = null;
const ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';
function requestAuthepyOTP() {
const email = document.getElementById('ath-email').value;
const formData = new URLSearchParams();
formData.append('action', 'authepy_request');
formData.append('email', email);
fetch(ajaxurl, { method: 'POST', body: formData })
.then(res => res.json())
.then(data => {
if (data.success) {
globalRequestId = data.data.requestId;
globalEmail = email;
// Display the OTP input field here
}
});
}
function verifyAuthepyOTP() {
}
</script>
<?php
return ob_get_clean();
}
?> Email OTP Verification for Webflow, Wix & Framer
Stop fake users and bots from ruining your email list or accessing your hidden content. Copy and paste our visual widget code to verify real human emails in under 3 seconds.
The "Hold Your Hand" Setup Guide
- Generate a Restricted Key
Log in to your dashboard, create a key under Restricted Domain Keys, type your website URL (e.g.,
mysite.com), and copy it. - Place the Widget on your Page
Add an HTML "Embed" element or "HTML iframe" block in Webflow or Wix, paste the layout snippet, and replace
YOUR_RESTRICTED_KEY_HERE.
<div id="authepy-widget" style="font-family: sans-serif; max-width: 320px; padding: 20px; border: 1px solid #ccc; border-radius: 8px;">
<div id="ath-step-1">
<h3>Secure Login</h3>
<input type="email" id="ath-email" placeholder="name@company.com" style="width: 100%; padding: 10px; margin-bottom: 10px;" />
<button onclick="authepySendOTP()" style="width: 100%; padding: 10px; background: #000; color: #fff;">Continue with Email</button>
<p id="ath-error-1" style="color: red; font-size: 12px; margin-top: 8px;"></p>
</div>
<div id="ath-step-2" style="display: none;">
<p>Enter the 6-digit code sent to your email.</p>
<input type="text" id="ath-code" placeholder="000000" maxlength="6" style="width: 100%; padding: 10px; margin-bottom: 10px; text-align: center; letter-spacing: 5px;" />
<button onclick="authepyVerifyOTP()" style="width: 100%; padding: 10px; background: #28a745; color: #fff;">Verify Code</button>
<p id="ath-error-2" style="color: red; font-size: 12px; margin-top: 8px;"></p>
</div>
</div>
<script>
const AUTHEPY_API = 'https://api.authepy.com/api';
const AUTHEPY_KEY = 'YOUR_RESTRICTED_KEY_HERE';
let authepyRequestId = '';
async function authepySendOTP() {
const email = document.getElementById('ath-email').value;
const err = document.getElementById('ath-error-1');
if(!email) return;
try {
const res = await fetch(`${AUTHEPY_API}/otp/request`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${AUTHEPY_KEY}` },
body: JSON.stringify({ email })
});
const data = await res.json();
if (data.success) {
authepyRequestId = data.requestId;
document.getElementById('ath-step-1').style.display = 'none';
document.getElementById('ath-step-2').style.display = 'block';
} else {
err.innerText = data.error;
}
} catch (e) { err.innerText = 'Network connection failed.'; }
}
async function authepyVerifyOTP() {
const code = document.getElementById('ath-code').value;
const err = document.getElementById('ath-error-2');
if(!code || code.length < 5) return;
try {
const res = await fetch(`${AUTHEPY_API}/otp/verify`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${AUTHEPY_KEY}` },
body: JSON.stringify({ requestId: authepyRequestId, userGuess: code })
});
const data = await res.json();
if (data.success) {
document.getElementById('authepy-widget').innerHTML = '<h3 style="color: green; text-align: center;">Verified Successfully</h3>';
} else {
err.innerText = data.error;
}
} catch (e) { err.innerText = 'Network connection failed.'; }
}
</script> Email OTPs for Zapier, Make, & Bubble.
Every time a fake user triggers your workflow, it costs you money. Add Authepy to Zapier, Make, or Bubble to force users to verify their email before your expensive automations run.