WhatsApp has more than two billion active users worldwide, making it the most popular messaging platform on the planet. For businesses that rely on one-time passwords (OTPs) to authenticate users, that reach represents a massive opportunity. OTP messages sent over WhatsApp see higher open rates, faster read times, and a richer end-user experience compared to traditional SMS — no carrier filtering, no gray-route uncertainty, and a branded message thread your users already trust.
Today, Telnyx supports WhatsApp as a verification channel in the Verify API. You can now send OTP codes over WhatsApp with the same two-call flow you already use for SMS — no separate WhatsApp Business API integration, no template pre-approval headaches, and no changes to your verification logic.
What Is WhatsApp Verify?
The Telnyx Verify API handles the entire OTP lifecycle: generating a code, delivering it to the user, and validating the response. When you set the channel to whatsapp, Telnyx routes the OTP through WhatsApp instead of SMS. The message arrives in the user's WhatsApp inbox as a branded verification message, complete with a clear code and expiration notice.
From your application's perspective, the integration is identical to SMS-based verification. You make a POST request to start a verification, the user receives a code, and you make a second POST request to check it. Telnyx manages the WhatsApp Business API session, message template, and delivery tracking behind the scenes.
How It Works
The verification flow has four steps:
- Your app requests an OTP. You send a POST request to the Telnyx Verify API with the user's phone number, your Verify Profile ID, and
"type": "whatsapp"to specify the WhatsApp channel.
- Telnyx delivers the code via WhatsApp. Telnyx generates a one-time password, formats it into a WhatsApp verification message, and delivers it to the user's device. You receive webhook events as the message moves through the pipeline —
verify.sentwhen the message is dispatched,verify.deliveredwhen WhatsApp confirms receipt.
- The user submits the code. Your application collects the code from the user through your own UI — a web form, a mobile app input field, or any other interface.
- Your app verifies the code. You send a second POST request to the Telnyx Verify API with the phone number and the code the user entered. Telnyx validates it and returns a success or failure response. A
verify.completedwebhook event confirms the verification is finalized.
No WhatsApp session management. No template submissions. Two HTTP calls and you're done.
The Architecture
┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
│ Your App │ ──────► │ Telnyx Verify │ ──────► │ WhatsApp │
│ (Flask) │ │ API │ │ (user) │
└──────────────┘ └──────────────────┘ └──────────────┘
▲ │
│ │ (webhooks)
└─────────────────────────┘
Your Python Flask application sends verification requests to the Telnyx Verify API, which handles WhatsApp message delivery. Webhook events flow back to your application for delivery tracking and status updates.
Prerequisites
- Python 3.8 or higher (Python 3.11+ recommended)
- A Telnyx account — sign up at portal.telnyx.com for free trial credit
- A Verify Profile with WhatsApp enabled as a channel
ngrokor another tunnel for local webhook testing
Step 1: Clone and Configure
git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/whatsapp-verify-otp-python
pip install -r requirements.txt
cp .env.example .env
Edit .env with your credentials:
TELNYX_API_KEY=KEY_your_api_key_here
VERIFY_PROFILE_ID=your_verify_profile_id_here
PORT=5000
Step 2: Understand the Verification Endpoints
The application exposes three endpoints. The first starts a verification:
@app.route("/verify/start", methods=["POST"])
def start_verification():
data = request.get_json()
phone = data.get("phone_number")
resp = requests.post(
"https://api.telnyx.com/v2/verifications",
headers={
"Authorization": f"Bearer {TELNYX_API_KEY}",
"Content-Type": "application/json",
},
json={
"phone_number": phone,
"verify_profile_id": VERIFY_PROFILE_ID,
"type": "whatsapp",
},
)
if resp.ok:
return jsonify({"status": "sent", "phone": phone, "channel": "whatsapp"})
return jsonify({"error": resp.text}), resp.status_code
The type field controls the delivery channel. Set it to "whatsapp" to route the OTP through WhatsApp. Set it to "sms" for traditional SMS delivery. The rest of the request body stays the same either way.
Step 3: Verify the Code
The second endpoint checks the code the user submitted:
@app.route("/verify/check", methods=["POST"])
def check_verification():
data = request.get_json()
phone = data.get("phone_number")
code = data.get("code")
resp = requests.post(
f"https://api.telnyx.com/v2/verifications/by_phone_number/{phone}/actions/verify",
headers={
"Authorization": f"Bearer {TELNYX_API_KEY}",
"Content-Type": "application/json",
},
json={"code": code},
)
if resp.ok:
return jsonify({"status": "verified"})
return jsonify({"status": "invalid_code"}), 400
A 200 response means the code is valid. A 400 response means the code is incorrect or expired. That's the entire integration — two endpoints, two requests, and your users are verified.
Step 4: Track Delivery With Webhooks
The third endpoint receives webhook events as the verification progresses:
@app.route("/webhooks/verify", methods=["POST"])
def verify_webhook():
payload = request.get_json()
event_type = payload.get("data", {}).get("event_type", "")
phone = payload.get("data", {}).get("payload", {}).get("phone_number", "")
if event_type == "verify.sent":
# OTP dispatched to WhatsApp
pass
elif event_type == "verify.delivered":
# WhatsApp confirmed receipt
pass
elif event_type == "verify.completed":
# User submitted correct code
pass
elif event_type == "verify.failed":
# Verification failed
pass
return jsonify({"status": "ok"})
These events let you build real-time status tracking, trigger fallback logic (try WhatsApp first, fall back to SMS), or log verification outcomes for compliance — without polling.
Step 5: Run It
python app.py
In another terminal, expose your local server:
ngrok http 5000
Set the ngrok URL as your Verify Profile's webhook URL in the Telnyx Portal. Now trigger a verification:
curl -X POST http://localhost:5000/verify/start \
-H "Content-Type: application/json" \
-d '{"phone_number": "+15551234567"}'
The user receives a WhatsApp message with the OTP code. Once they enter it:
curl -X POST http://localhost:5000/verify/check \
-H "Content-Type: application/json" \
-d '{"phone_number": "+15551234567", "code": "12345"}'
A 200 response with "status": "verified" means the code matched.
Why This Pattern Works for Production
The example above is intentionally minimal, but the pattern scales. Three things make it production-ready:
1. Channel abstraction. The Verify API supports SMS, WhatsApp, and voice through the same endpoint. Switch channels by changing a single field in your request body. Build fallback chains — try WhatsApp first, fall back to SMS if the user isn't on WhatsApp — without managing separate integrations.
2. Webhook-driven delivery tracking. Instead of polling for status, your application receives real-time events as the OTP moves through the pipeline. You know exactly when the message was sent, delivered, and verified — or when it failed.
3. No WhatsApp Business API management. Telnyx handles WhatsApp Business API connectivity, message templates, and session management. You don't need to apply for WhatsApp access, submit templates for approval, or manage per-country configurations. The Verify API abstracts all of that.
Extending the Example
The base example covers the happy path. Real verification flows need more:
- Fallback channels — If WhatsApp delivery fails, automatically retry via SMS using the same API.
- Rate limiting — Add per-phone-number rate limits to prevent abuse.
- Code expiration — Configure the Verify Profile to set custom expiration windows.
- Audit logging — Log all webhook events for compliance and fraud detection.
Each of these is a small addition on top of the same Verify API.
The Bigger Picture: Unified Verification Infrastructure
WhatsApp Verify is a signal of how verification infrastructure is converging. The old model — one integration per channel, one vendor per country, one API per message type — is being replaced by unified APIs that handle routing, delivery, and validation across channels.
Telnyx is AI Communications Infrastructure. The same API key that sends WhatsApp OTPs also handles SMS, voice calls, SIP trunking, and AI inference. One platform, one bill, one support team that owns the entire delivery path from your application to the user's device.
Get the Code
The full example is in the telnyx-code-examples repository under whatsapp-verify-otp-python. It includes:
app.py— the complete Flask applicationREADME.md— quickstart and API reference.env.example— environment variable template
Clone it, add your API key and Verify Profile ID, and you have a working WhatsApp OTP service.