Documentation
Flip OTP verifies a phone number by having the user send a short code from WhatsApp to your business number. This guide covers the widget, the proof token and the HTTP API.
Quickstart
One script tag, one input, one callback. That is the whole integration.
<script src="https://app.flipotp.com/flipotp.js"></script>
<input class="js-phone" name="phone" type="tel" placeholder="+60 12 345 6789" />
<script>
FlipOTP.init({
key: 'fpo_live_xxxxxxxxxxxxxxxx',
input: '.js-phone', // CSS selector, element, or array of either
defaultCountry: 'MY', // used when the user omits a country code
tokenInput: 'flipotp_token', // hidden field the proof is written to
onVerified: (token, { phone }) => {
console.log('verified', phone, token);
},
onError: (message) => console.warn(message),
});
</script>
flipotp_token. Submit it
with your form and verify it server-side — never trust the badge alone.
Install
Add the script to any page. The widget is a vanilla JavaScript bundle, isolated in a shadow DOM, and works with React, Vue, Svelte, Astro, Rails, Laravel and plain HTML.
<script src="https://app.flipotp.com/flipotp.js" defer></script>
You can also initialise declaratively by putting attributes on the script tag:
<script
src="https://app.flipotp.com/flipotp.js"
data-flipotp-key="fpo_live_xxxxxxxx"
data-flipotp-input=".js-phone"
data-flipotp-country="MY"
></script>
Widget options
key— your public site key. Required.input— a CSS selector, an input element, or an array of either. Required.defaultCountry— ISO country code applied when the number has no country prefix. Defaults toMY.tokenInput— name of the hidden field that receives the proof. Defaults toflipotp_token.apiBase— override the API origin. Only useful for self-hosted or staging.onVerified(token, { phone })— called when the number is verified.onError(message)— called for validation and network errors.
The widget also emits a flipotp:verified DOM event on the phone input, with
detail.token and detail.phone.
How verification works
- Clicking Verify asks the API for a 6-character code, bound to the phone number and your site.
- The code is stored for 100 seconds. One active code per number per site.
- The modal shows the code, a
wa.medeep link and a QR code. - The user sends that code from their own WhatsApp to the Flip OTP business number.
- The webhook accepts it only when the sender number equals the number being verified — this is the entire security model.
- The API signs a proof with your site secret and hands it to the widget.
Verify the proof
Tokens are base64url(payload).base64url(HMAC-SHA256), signed with your site
secret. They expire after 15 minutes by default.
// Node / any runtime with WebCrypto — no SDK required
import { verifyProof } from '@flipotp/core';
const result = await verifyProof(token, process.env.FLIPOTP_SITE_SECRET);
if (!result) return respond(400, 'Invalid or expired proof');
if (result.phone !== submittedPhone) return respond(400, 'Phone mismatch');
// result: { acc, site, phone, vid, iat, exp } — all identifiers are UUIDs
Prefer not to embed crypto? Validate online:
POST https://app.flipotp.com/v1/verify/token
{ "token": "<proof>" }
→ { "valid": true, "phone": "+60123456789", "siteUuid": "ste-…",
"verificationUuid": "ver-…", "expiresAt": "…" }
API reference
Start a verification
POST https://app.flipotp.com/v1/verify/start
{ "key": "fpo_live_…", "phone": "+60123456789", "country": "MY" }
→ { "id": "ver-…", "code": "K7M2QP",
"waLink": "https://wa.me/60123…?text=K7M2QP",
"whatsappNumber": "60123…", "expiresAt": "…", "ttlSeconds": 100 }
GET https://app.flipotp.com/v1/verify/status?id=ver-…
→ { "status": "pending" | "verified" | "expired", "proof": "…" }
Errors
400— invalid phone number or missing fields.402— verification quota reached. Upgrade to continue.403— domain not allowed for this site key.404— unknown site key.429— rate limited. Retry after the window.
Limits
Defaults on the shared WhatsApp number: one active code per phone per site, five codes per phone per hour, 60 codes per site per minute and 600 per account per minute. Superadmins can tune every value, and plan limits are enforced before a code is issued.
WhatsApp setup
On the Dev and SME plans you send codes to the shared Flip OTP WhatsApp Business number. The number is shown in the modal and in every site's settings page. No Meta app review is needed on your side.
The SaaS plan can use a white-labelled or per-tenant WhatsApp number. Contact us to provision it.