WappSync Phone Verify — API Docs
Phone verification where your user sends a code to us via WhatsApp — no OTP typing, one tap, fully verified.
Contents
How It Works
You request a code
Your backend calls our API with the user's phone number. We return a unique code and the WhatsApp number to send it to.
User sends it via WhatsApp
Your user taps a WhatsApp link — the code is pre-filled. No typing, no copy-paste. They just hit send.
Verified instantly
We receive the message, confirm it came from the correct phone, and your poll returns auth=1. Done!
Get Your Credentials
Sign up at wappsync.com/mybusiness to receive:
| Credential | Description |
|---|---|
businessid | Your numeric business ID |
businesskey | Your secret key — never share this, never put it in client-side code |
businesskey is used for HMAC signing and must never appear in browser JavaScript, HTML source, or mobile app code.Request a Reverse OTP
Call this from your backend when you need to verify a user:
POSThttps://wappsync.com/phone-verify/api/getReverseOTP
Request Body (JSON)
| Field | Type | Description |
|---|---|---|
businessid | int | Your business ID |
businessref | string | A unique reference you generate for this auth request (e.g., bin2hex(random_bytes(4))) |
phone | string | The user's phone number in international format (+40755123456) |
ts | int | Current Unix timestamp |
hmac | string | HMAC-SHA256 signature |
HMAC Signing
Sign this string with your businesskey:
{businessid}|{businessref}|{phone}|{ts}
Example:
$hmac = hash_hmac('sha256', "{$businessid}|{$businessref}|{$phone}|{$ts}", $businesskey);
Response
| Field | Type | Description |
|---|---|---|
status | string | "ok" or "error" |
reverseotp_display | string | The code your user sends, e.g. "2FA-828161" |
waphone | string | The WhatsApp number your user sends the code TO |
logincode | string | Internal code to use when polling |
in_ts | int | Timestamp for the polling request |
out_ts | int | Response timestamp |
otp_valid_minutes | int | How long the code is valid (default 10) |
hmac | string | HMAC of the response — verify with your businesskey |
Verify the response HMAC against {businessid}|{businessref}|{logincode}|{reverseotp_display}|{waphone}|{out_ts}.
What to show your user
Display the code and phone number. Use a whatsapp://send link so the user can send it in one tap:
<a href="whatsapp://send?phone=40725296320&text=2FA-828161">
💬 Send Code via WhatsApp
</a>
Poll for Verification
Poll every 2-3 seconds until the user is verified:
POSThttps://wappsync.com/phone-verify/api/getReverseOTPStatus
Request Body (JSON)
| Field | Type | Description |
|---|---|---|
businessid | int | Your business ID |
businessref | string | The same reference from the previous call |
logincode | string | The logincode from the getReverseOTP response |
in_ts | int | The original ts from the getReverseOTP request |
hmac | string | HMAC-SHA256 of {businessid}|{businessref}|{logincode}|{in_ts} |
Response
| Field | Type | Meaning |
|---|---|---|
used | 0 | ⏳ Still waiting — poll again |
used | 1 | ✅ Just verified — the first poll to see this consumes it |
used | 2 | 🔒 Already consumed by a previous poll |
auth | 1 | User is verified — log them in! |
phoneEnc | string | AES-256-CBC encrypted phone number (only when auth=1) |
tries | int | Failed attempts counted (wrong code or wrong phone) |
expired | bool | true when too many failed attempts (tries ≥ 4) — request a new OTP |
out_ts | int | Response timestamp |
hmac | string | HMAC — verify against {businessid}|{businessref}|{logincode}|{used}|{phoneEnc}|{out_ts} |
When auth === 1, the phone number is proven to belong to the user. Log them in.
Decrypt the Phone Number
When auth=1, phoneEnc contains the verified phone number encrypted with AES-256-CBC using your businesskey:
function decryptPhoneAES($encryptedData, $businesskey) {
$method = 'AES-256-CBC';
$key = hash('sha256', $businesskey, true);
$enc = str_replace(['-', '_'], ['+', '/'], $encryptedData);
$enc = str_pad($enc, strlen($enc) % 4 === 0
? strlen($enc) : strlen($enc) + 4 - strlen($enc) % 4,
'=', STR_PAD_RIGHT);
$decoded = base64_decode($enc);
$iv = substr($decoded, 0, 16);
$ciphertext = substr($decoded, 16);
return openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);
}
$phone = decryptPhoneAES($response['phoneEnc'], $businesskey);
// Store this phone — it's the user's verified identity
Frontend Example
The only UI you need — show after your backend calls getReverseOTP:
<div style="text-align:center; max-width:400px; margin:auto;">
<p>Send this exact code via WhatsApp from your phone:</p>
<div style="font-size:28px; font-weight:bold; color:#25D366;
letter-spacing:3px;">
2FA-828161
</div>
<p>to <strong>+40 725 296 320</strong></p>
<a href="whatsapp://send?phone=40725296320&text=2FA-828161"
style="display:inline-block; padding:14px 30px; background:#25D366;
color:white; font-size:18px; font-weight:bold; border-radius:8px;
text-decoration:none; margin-top:12px;">
💬 Send via WhatsApp
</a>
<p style="color:#888; font-size:13px; margin-top:12px;">
⏱ Code expires in 10 minutes — no typing required!
</p>
</div>
Complete PHP Example
Copy-paste ready. Replace YOUR_BUSINESS_ID and 'your-secret-key' with the values from wappsync.com/mybusiness.
<?php
$businessid = YOUR_BUSINESS_ID; // from wappsync.com/mybusiness
$businesskey = 'your-secret-key'; // from wappsync.com/mybusiness — keep server-side
// ── Step 1: Request reverse OTP ──
$businessref = bin2hex(random_bytes(4));
$phone = $_POST['phone']; // user's phone from your form
$ts = time();
$hmac = hash_hmac('sha256',
"{$businessid}|{$businessref}|{$phone}|{$ts}",
$businesskey
);
$response = callAPI('https://wappsync.com/phone-verify/api/getReverseOTP', [
'businessid' => $businessid,
'businessref' => $businessref,
'phone' => $phone,
'ts' => $ts,
'hmac' => $hmac,
]);
$data = json_decode($response, true);
$code = $data['reverseotp_display']; // "2FA-828161"
$waphone = $data['waphone']; // "+40725296320"
$logincode = $data['logincode'];
$in_ts = $data['in_ts'];
// ── Show the code to your user (render frontend example above) ──
// ── Step 2: Poll until verified ──
$maxAttempts = 40; // ~2 minutes at 3s intervals
$attempt = 0;
do {
sleep(3);
$attempt++;
$hmac = hash_hmac('sha256',
"{$businessid}|{$businessref}|{$logincode}|{$in_ts}",
$businesskey
);
$response = callAPI(
'https://wappsync.com/phone-verify/api/getReverseOTPStatus',
[
'businessid' => $businessid,
'businessref' => $businessref,
'logincode' => $logincode,
'in_ts' => $in_ts,
'hmac' => $hmac,
]
);
$data = json_decode($response, true);
} while (($data['auth'] ?? 0) !== 1 && $attempt < $maxAttempts);
if (($data['auth'] ?? 0) === 1) {
// ✅ Verified — log the user in!
$phone = decryptPhoneAES($data['phoneEnc'], $businesskey);
$_SESSION['user_phone'] = $phone;
header('Location: /dashboard');
exit;
} else {
echo "Verification timed out. Please try again.";
}
// ── Helper ──
function callAPI($url, $payload) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
Error Handling
The API returns "status": "error" with a human-readable message:
| HTTP | message | When |
|---|---|---|
| 400 | Missing required parameters | You forgot a field |
| 400 | The phone number is invalid or not a mobile number. | Bad phone format |
| 403 | Unknown or invalid business ID | Wrong businessid |
| 403 | Invalid HMAC | Signature doesn't match — check your signing string |
| 409 | This request was already used. | Duplicate businessref+in_ts |
Security
| 🔐 HMAC-signed | Every request and response is verified with your businesskey |
| 📱 Phone matching | We verify the WhatsApp sender's number matches the one you requested |
| 🔂 Single-use | Codes go 0→1→2, never reusable |
| ⏱ Time-limited | Codes expire after 10 minutes, max 4 delivery attempts |
| 🔒 No client secrets | Your businesskey stays on your server; never in browser code |
| 🛡 AES-256 encrypted | Phone numbers in responses are encrypted with your businesskey |
Try It Live
🎮 Demo Login Flow — a working demo showing the complete user experience.
🧪 API Testing Tool — requires your own businessid and businesskey from wappsync.com/mybusiness.