DIDfarm
  • Numbers
  • Trunks
  • Messaging
  • Connect
  • Pricing
  • Coverage
  • Help
🇬🇧 EN 🇳🇱 NL 🇩🇪 DE 🇫🇷 FR 🇪🇸 ES 🇧🇷 PT 🇸🇦 AR 🇨🇳 ZH 🇯🇵 JA 🇮🇳 HI
Sign in Get a Number
← Help Center
On this page
Overview Why WhatsApp for OTP Prerequisites 1 — Create Auth Template 2 — Send OTP 3 — Verify the Code Copy Code Button SMS Fallback Best Practices FAQ

OTP & 2FA via WhatsApp

Messaging · 10 min read OTP WhatsApp API
What this guide covers: How to send one-time passwords via WhatsApp using Meta's authentication template category. Includes template setup, API examples, copy-code buttons, SMS fallback logic, and rate limiting best practices.

Overview

WhatsApp authentication templates let you send OTP codes that arrive as rich messages with a one-tap copy button. Users don't need to manually read and type the code — they tap "Copy Code" and paste it into your app.

WhatsApp OTPs are delivered faster than SMS in most markets, cost less in many regions, and have near-100% open rates because users actively check WhatsApp throughout the day.

Why WhatsApp for OTP

FactorSMSWhatsApp
Open rate~95%~99%
Delivery speed1-5 seconds<1 second
Copy-code buttonNo (manual typing)Yes (one-tap copy)
Sender identityNumber or alphanumericVerified business profile
Cost (EU)~€0.008~€0.035 (auth template)
Cost (India)~€0.005~€0.003 (auth template)
Requires appNoYes (WhatsApp installed)
Best of both worlds: Send via WhatsApp first, fall back to SMS after 10 seconds if delivery isn't confirmed. This gives you WhatsApp's speed and UX where available, with SMS as a universal safety net.

Prerequisites

  • A WhatsApp Business Account connected to DIDfarm (setup guide)
  • An approved authentication template in your Meta WABA
  • A DIDfarm API key (Dashboard → API Keys)
  • An SMS-enabled number for fallback (optional but recommended)

Step 1 — Create an Authentication Template

1Create a template in Meta Business Manager

Go to Meta Business Suite → WhatsApp Manager → Message Templates → Create Template.

  • Category: Authentication
  • Template name: login_otp
  • Language: English (add more as needed)

Meta provides a standard authentication template format. The body is auto-generated:

TEMPLATE BODY (AUTO-GENERATED BY META)
1 is your verification code.

Security: Do not share this code.

Where 1 is replaced with your OTP code at send time. You can optionally add:

  • Copy code button — adds a "Copy Code" button that copies the OTP to clipboard
  • Code expiry — shows "This code expires in X minutes" below the message
Template approval: Authentication templates are usually approved within minutes. Do not include marketing content or links — Meta will reject the template.

Step 2 — Send OTP via WhatsApp

2Send the authentication template via API
NODE.JS — SEND WHATSAPP OTP
const code = crypto.randomInt(100000, 999999).toString();

// Store code with 5-minute expiry
await redis.setex(`otp:${phoneNumber}`, 300, code);

// Send via WhatsApp
const response = await fetch('https://didfarm.com/api/v1/whatsapp/messages', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    account_id: 1,                    // Your WhatsApp account ID
    to: '+31611398058',
    type: 'template',
    template: {
      name: 'login_otp',
      language: { code: 'en' },
      components: [{
        type: 'body',
        parameters: [{ type: 'text', text: code }]
      }, {
        type: 'button',
        sub_type: 'url',
        index: 0,
        parameters: [{ type: 'text', text: code }]
      }]
    }
  }),
});

const data = await response.json();
console.log(data.id, data.status); // message ID, "accepted"
PYTHON
import secrets, requests

code = str(secrets.randbelow(900000) + 100000)
redis_client.setex(f"otp:{phone_number}", 300, code)

resp = requests.post(
    'https://didfarm.com/api/v1/whatsapp/messages',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
    json={
        'account_id': 1,
        'to': '+31611398058',
        'type': 'template',
        'template': {
            'name': 'login_otp',
            'language': {'code': 'en'},
            'components': [{
                'type': 'body',
                'parameters': [{'type': 'text', 'text': code}]
            }, {
                'type': 'button',
                'sub_type': 'url',
                'index': 0,
                'parameters': [{'type': 'text', 'text': code}]
            }]
        }
    },
)

print(resp.json()['id'], resp.json()['status'])

Step 3 — Verify the Code

3Compare user input with stored code

The verification logic is identical to SMS OTP — compare the user's input against the stored code in Redis or your database.

NODE.JS — VERIFY OTP
async function verifyOTP(phoneNumber, userInput) {
  const stored = await redis.get(`otp:${phoneNumber}`);

  if (!stored) return { valid: false, error: 'Code expired' };

  if (stored !== userInput) {
    const attempts = await redis.incr(`otp_attempts:${phoneNumber}`);
    await redis.expire(`otp_attempts:${phoneNumber}`, 300);
    if (attempts >= 5) {
      await redis.del(`otp:${phoneNumber}`);
      return { valid: false, error: 'Too many attempts' };
    }
    return { valid: false, error: 'Invalid code' };
  }

  await redis.del(`otp:${phoneNumber}`);
  await redis.del(`otp_attempts:${phoneNumber}`);
  return { valid: true };
}

Copy Code Button

When you configure the authentication template with a copy code button, the user sees a "Copy Code" button below the message. Tapping it copies the OTP to their clipboard so they can paste it into your app without typing.

This significantly improves conversion rates — users don't mistype digits or switch between apps to manually copy.

Pro tip: On Android, if your app uses the SMS Retriever API equivalent for WhatsApp, the code can be auto-filled without any user interaction at all.

SMS Fallback

Not all users have WhatsApp installed. Implement a fallback to SMS if WhatsApp delivery isn't confirmed within 10 seconds.

NODE.JS — WHATSAPP-FIRST, SMS FALLBACK
async function sendOTP(phoneNumber, code) {
  // 1. Try WhatsApp first
  const waResponse = await sendWhatsAppOTP(phoneNumber, code);

  // 2. Wait for delivery confirmation (max 10 seconds)
  const delivered = await pollDeliveryStatus(waResponse.id, {
    timeout: 10000,
    interval: 2000,
  });

  if (delivered) {
    return { channel: 'whatsapp', messageId: waResponse.id };
  }

  // 3. Fall back to SMS
  const smsResponse = await fetch('https://didfarm.com/api/v1/sms/messages', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      from_number_id: 42,
      to: phoneNumber,
      body: `Your code is ${code}. Expires in 5 minutes.`,
    }),
  });

  return { channel: 'sms', messageId: (await smsResponse.json()).id };
}

Best Practices

  • Use 6-digit codes — matches industry standard and works with copy-code button
  • Set 5-minute expiry — long enough to switch apps, short enough to be secure
  • Rate limit to 1 OTP per 60 seconds per number, 5 per hour
  • Always implement SMS fallback for users without WhatsApp
  • Track which channel delivered successfully to optimize future sends per user
  • Use a dedicated number for OTPs — don't mix authentication and marketing on the same number
  • Log the channel, delivery time, and verification result for fraud detection

FAQ

Is WhatsApp OTP cheaper than SMS?

It depends on the country. In India and Brazil, WhatsApp authentication templates are significantly cheaper than SMS. In the EU, SMS is often cheaper. Use the fallback pattern to optimize cost: try WhatsApp first in low-cost markets, SMS first in high-cost WhatsApp markets.

What happens if the user hasn't opted in?

Authentication templates are exempt from the usual marketing opt-in requirement. You can send OTP codes to any WhatsApp number without prior consent. However, the user must have WhatsApp installed and the number must be registered.

Can I customize the OTP message?

Authentication templates have a fixed format set by Meta. You can only customize the security disclaimer text and whether to include a copy-code button and expiry timer. The body format (1 is your verification code) cannot be changed.

How do I track delivery?

DIDfarm sends delivery status webhooks for WhatsApp messages: sent, delivered, read, and failed. Set up a webhook endpoint in your WhatsApp account settings to receive these updates.

Ready to integrate WhatsApp OTP?

Check the full WhatsApp setup guide for account connection and template management.

WhatsApp Guide →
© 2026 DIDfarm · didfarm.com
About Blog Partners Coverage API Docs Status Privacy Terms Cookies Help

We use essential cookies to make DIDfarm work. With your consent, we also use analytics cookies to improve our service. Cookie Policy