The flow in plain words
Your user types their mobile number. Your server (never the browser or the app) calls /send. We generate a 6-digit code, send it on WhatsApp from your number, and hand you back a request_id. The user types the code; your server calls /check with both. If the answer is "verified": true, log them in.
You never see or store the code itself. That's deliberate: if your database leaks, there's nothing useful in it.
PHP (Laravel)
$res = Http::withToken(env('AUTOCHATLY_OTP_KEY'))
->post('https://app.autochatly.com/api/verify/v1/send', ['phone' => $request->phone]);
if ($res->failed()) {
// e.g. send_failed: number not on WhatsApp, so fall back to SMS here
return back()->withErrors(['phone' => $res['message']]);
}
session(['otp_request' => $res['request_id']]);
// later, when the user submits the code
$check = Http::withToken(env('AUTOCHATLY_OTP_KEY'))
->post('https://app.autochatly.com/api/verify/v1/check', [
'request_id' => session('otp_request'),
'code' => $request->code,
]);
$ok = $check->ok() && $check['verified'] === true;
Node.js
const api = (path, body) => fetch(`https://app.autochatly.com/api/verify/v1/${path}`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.AUTOCHATLY_OTP_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify(body),
}).then(r => r.json());
const { ok, request_id, message } = await api('send', { phone: '+919876543210' });
// store request_id in the session; show `message` to the user if !ok
const result = await api('check', { request_id, code: userTypedCode });
if (result.verified === true) { /* signed in */ }
Python
import os, requests
H = {"Authorization": f"Bearer {os.environ['AUTOCHATLY_OTP_KEY']}"}
BASE = "https://app.autochatly.com/api/verify/v1"
r = requests.post(f"{BASE}/send", json={"phone": "+919876543210"}, headers=H, timeout=10).json()
request_id = r.get("request_id")
v = requests.post(f"{BASE}/check", json={"request_id": request_id, "code": code}, headers=H, timeout=10).json()
verified = v.get("verified") is True
Falling back to SMS
If /send returns send_failed, the number usually isn't on WhatsApp. That's your cue to send an SMS through your existing provider. Don't fall back on too_soon or phone_limit: those are rate limits doing their job, often against a bot.
Mistakes we see in the first week
- Calling the API from the mobile app. Anyone can pull the key out of an APK. Always call from your backend.
- No payment method in Meta. Template messages, OTPs included, are blocked until a card is added to the WhatsApp account.
- Trusting the client. Mark a number verified only after your server gets
verified: true. - Leaving every country open. Keep the allow-list to the countries you actually serve.
Full request and error reference: WhatsApp OTP API docs. Budgeting: WhatsApp OTP pricing. Product overview: WhatsApp OTP API.