WappSync
A

WappSync Phone Verify — API Docs

Phone verification where your user sends a code to us via WhatsApp — no OTP typing, one tap, fully verified.

How It Works

1

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.

2

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.

3

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:

CredentialDescription
businessidYour numeric business ID
businesskeyYour secret key — never share this, never put it in client-side code
Important: All API calls must come from your backend. The 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)

FieldTypeDescription
businessidintYour business ID
businessrefstringA unique reference you generate for this auth request (e.g., bin2hex(random_bytes(4)))
phonestringThe user's phone number in international format (+40755123456)
tsintCurrent Unix timestamp
hmacstringHMAC-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

FieldTypeDescription
statusstring"ok" or "error"
reverseotp_displaystringThe code your user sends, e.g. "2FA-828161"
waphonestringThe WhatsApp number your user sends the code TO
logincodestringInternal code to use when polling
in_tsintTimestamp for the polling request
out_tsintResponse timestamp
otp_valid_minutesintHow long the code is valid (default 10)
hmacstringHMAC 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)

FieldTypeDescription
businessidintYour business ID
businessrefstringThe same reference from the previous call
logincodestringThe logincode from the getReverseOTP response
in_tsintThe original ts from the getReverseOTP request
hmacstringHMAC-SHA256 of {businessid}|{businessref}|{logincode}|{in_ts}

Response

FieldTypeMeaning
used0⏳ Still waiting — poll again
used1✅ Just verified — the first poll to see this consumes it
used2🔒 Already consumed by a previous poll
auth1User is verified — log them in!
phoneEncstringAES-256-CBC encrypted phone number (only when auth=1)
triesintFailed attempts counted (wrong code or wrong phone)
expiredbooltrue when too many failed attempts (tries ≥ 4) — request a new OTP
out_tsintResponse timestamp
hmacstringHMAC — 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:

HTTPmessageWhen
400Missing required parametersYou forgot a field
400The phone number is invalid or not a mobile number.Bad phone format
403Unknown or invalid business IDWrong businessid
403Invalid HMACSignature doesn't match — check your signing string
409This request was already used.Duplicate businessref+in_ts

Security

🔐 HMAC-signedEvery request and response is verified with your businesskey
📱 Phone matchingWe verify the WhatsApp sender's number matches the one you requested
🔂 Single-useCodes go 0→1→2, never reusable
Time-limitedCodes expire after 10 minutes, max 4 delivery attempts
🔒 No client secretsYour businesskey stays on your server; never in browser code
🛡 AES-256 encryptedPhone 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.