WhatsApp Cloud API

How to Add WhatsApp OTP Login to Your Website at the Lowest Cost (Using Meta Cloud API)

A complete, code-level guide to building WhatsApp OTP login directly on Meta Cloud API — skip expensive third-party OTP providers and keep full control over cost, delivery speed, and user data.

Diagram showing a WhatsApp OTP login flow between a website, a database, and the Meta Cloud API

If you've priced out WhatsApp OTP login for your website, you've probably seen monthly platform fees on top of per-message charges from providers like Twilio, MSG91, Interakt, AiSensy, or 360dialog. I've personally integrated all of these for clients at one point or another. What I found, repeatedly, is that once you understand Meta's own Cloud API, most of that provider markup is optional — not necessary.

This guide walks through exactly how to build WhatsApp OTP login yourself, directly against Meta Cloud API, with real PHP and Laravel code, production security practices, and the mistakes I've made so you don't have to repeat them.

Table of contents

What is WhatsApp OTP?

WhatsApp OTP is a one-time password sent to a user's WhatsApp number instead of by SMS or email, used to verify a login, a signup, or a sensitive action like a password reset. The user receives a code inside WhatsApp, types it into your website, and your backend checks it against what it generated.

Functionally, it's identical to SMS OTP from the user's point of view. The difference is entirely in the delivery channel, the cost structure, and — in my experience — the delivery reliability.

Why businesses prefer WhatsApp OTP

Across the projects I've worked on, three reasons come up constantly when clients ask for WhatsApp OTP instead of SMS:

  • Delivery reliability. SMS OTPs are frequently delayed or silently dropped by mobile carriers, especially for DND-registered numbers in India. WhatsApp messages don't pass through the same carrier filtering.
  • Cost. In most markets I've billed for, WhatsApp authentication conversations cost less than an equivalent SMS, particularly for international numbers where SMS costs spike.
  • User trust. Users increasingly recognize and trust a verified WhatsApp Business message more than an SMS from an unknown short code.

SMS OTP vs WhatsApp OTP

FactorSMS OTPWhatsApp OTP
Typical delivery speedSeconds to minutes, carrier-dependentNear-instant while WhatsApp is online
Delivery reliabilityAffected by DND/carrier filteringNot subject to telecom DND filtering
Cost per message (typical)Higher in most marketsLower for domestic authentication conversations
Requires internetNoYes
Works without WhatsApp installedYesNo — needs fallback

Neither channel is strictly better. In my implementations, the most resilient setup sends WhatsApp OTP first and falls back to SMS only if the WhatsApp message fails to deliver, which the Cloud API webhook will tell you.

Why many businesses spend more than necessary

While implementing OTP systems for different clients, I noticed a consistent pattern: businesses sign up with a third-party OTP provider because it's the fastest way to get started, then never revisit that decision once volume grows. The provider's dashboard, quick-start SDKs, and support are genuinely valuable at day one — but that same convenience keeps costing a recurring markup on every single message, indefinitely.

None of this is criticism of those providers — they built real infrastructure and support that has value, especially for teams without in-house developers. But if you already have a PHP or Laravel developer on your team, or are paying one anyway for your website, the marginal cost of removing the middle layer is small compared to what it saves over the life of the product.

How third-party providers work

Providers like Twilio, MSG91, Interakt, AiSensy, and 360dialog are, at the infrastructure level, all built on top of the same Meta WhatsApp Business Platform. They act as a Meta Business Solution Provider (BSP), meaning they already hold the underlying relationship with Meta and resell access to it through their own dashboard, SDKs, and billing.

You send your OTP request to their API, they translate it into a Meta Cloud API call using their own Meta connection, and they bill you their rate — which includes Meta's underlying cost plus their margin for the tooling and support layered on top.

Direct Meta Cloud API architecture

Cutting out the middle layer means your own server talks to Meta directly. The flow looks like this:

User enters phone number
        ↓
   Your Website
        ↓
  Generate 6-digit OTP
        ↓
  Hash OTP → Save to Database
        ↓
  Call Meta Cloud API (Authentication Template)
        ↓
      WhatsApp
        ↓
  User receives OTP, enters it on your site
        ↓
  Your Website verifies hash against Database
        ↓
      Login / Signup complete

Everything after "Generate 6-digit OTP" happens on infrastructure you control. There's no third-party billing layer between your server and Meta.

Required accounts and setup

Before writing any code, you need the following set up in this order:

  1. A Meta Business Account (business.facebook.com)
  2. A WhatsApp Business Platform app created inside Meta for Developers
  3. A dedicated business phone number, verified via SMS or voice call through Meta, not currently active on regular WhatsApp or WhatsApp Business consumer apps
  4. At least one approved Authentication template
  5. A System User with a permanent access token generated in Meta Business Settings
  6. A publicly reachable HTTPS webhook URL on your own server

Phone number verification

In practice, the phone number verification step is usually the fastest part — Meta sends a code via SMS or automated call to confirm you control the number. The number then becomes permanently tied to that WhatsApp Business Account and can't be reused on the personal WhatsApp app afterward.

Authentication templates

Meta requires OTP messages to use a specific template category called Authentication. Unlike marketing or utility templates, authentication templates have a restricted, simplified format specifically designed to prevent misuse — you can't add promotional text, images, or extra body copy.

A typical approved authentication template body looks like this:

Your verification code is {{1}}. Do not share this code with anyone.

One challenge I repeatedly encountered early on: submitting a template with extra branding language ("Welcome to [Company]! Your code is...") gets rejected, because Meta's authentication category strictly limits the template to the verification code and a security warning. Keep it minimal and it approves quickly.

Permanent access tokens

Meta's default access tokens generated from the API Setup screen expire in 24 hours, which is unusable for a production server. The fix is creating a System User inside Meta Business Settings, assigning it to your WhatsApp Business app with the correct permissions, and generating a token from that System User instead — these don't expire on the same 24-hour cycle.

I found that this step is where most first-time integrations get stuck: people copy the temporary token from the quick-start page, it works for a day, then silently breaks in production. Always generate your production token from a System User, not the quick-start screen.

Webhook verification

To receive delivery status updates (sent, delivered, read, failed) and inbound replies, Meta requires a webhook endpoint on your server. Setting it up involves a verification handshake:

// webhook.php — GET verification handshake
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    $verifyToken = 'your-own-secret-string';
    if (($_GET['hub_verify_token'] ?? '') === $verifyToken) {
        echo $_GET['hub_challenge'] ?? '';
        exit;
    }
    http_response_code(403);
    exit;
}

Once verification succeeds, Meta starts POSTing JSON payloads to the same URL for every status event. In production, this is what tells you whether an OTP actually reached the user's device — critical for deciding when to trigger an SMS fallback.

Database design

Every OTP implementation I've built uses some version of this table. Each field earns its place:

FieldPurpose
idPrimary key
phoneThe recipient's phone number in E.164 format
otp_hashThe bcrypt hash of the OTP — never the plain code
expires_atTimestamp after which this OTP is no longer valid, typically now + 5 minutes
attemptsNumber of failed verification attempts made against this record
verifiedBoolean flag set to true once successfully verified, preventing replay
created_at / updated_atStandard audit timestamps
CREATE TABLE otp_verifications (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    phone VARCHAR(20) NOT NULL,
    otp_hash VARCHAR(255) NOT NULL,
    expires_at DATETIME NOT NULL,
    attempts TINYINT UNSIGNED NOT NULL DEFAULT 0,
    verified TINYINT UNSIGNED NOT NULL DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_phone (phone)
);

PHP implementation

Here's the core logic, broken into the steps a login flow actually needs. I'm using plain PDO here so it's portable to any PHP project, Laravel or not.

1. Generate the OTP

function generateOtp(): string {
    return str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
}

random_int() is cryptographically secure — never use rand() or mt_rand() for anything security-related.

2. Hash and store it

$otp  = generateOtp();
$hash = password_hash($otp, PASSWORD_BCRYPT);

$stmt = $pdo->prepare(
    "INSERT INTO otp_verifications (phone, otp_hash, expires_at, attempts, verified)
     VALUES (?, ?, ?, 0, 0)"
);
$stmt->execute([$phone, $hash, date('Y-m-d H:i:s', time() + 300)]);

3. Send it via the Authentication Template

function sendWhatsAppOtp(string $phone, string $otp, string $phoneNumberId, string $accessToken): array {
    $url = "https://graph.facebook.com/v20.0/{$phoneNumberId}/messages";

    $payload = [
        'messaging_product' => 'whatsapp',
        'to'                => $phone,
        'type'              => 'template',
        'template'          => [
            'name'       => 'otp_login',
            'language'   => ['code' => 'en_US'],
            'components' => [
                ['type' => 'body', 'parameters' => [['type' => 'text', 'text' => $otp]]],
            ],
        ],
    ];

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            "Authorization: Bearer {$accessToken}",
            "Content-Type: application/json",
        ],
        CURLOPT_POSTFIELDS => json_encode($payload),
    ]);

    $response = curl_exec($ch);
    curl_close($ch);

    return json_decode($response, true) ?? [];
}

4. Verify what the user enters

$stmt = $pdo->prepare(
    "SELECT * FROM otp_verifications WHERE phone = ? ORDER BY id DESC LIMIT 1"
);
$stmt->execute([$phone]);
$record = $stmt->fetch(PDO::FETCH_ASSOC);

if (!$record || $record['verified']) {
    // no active OTP for this number
} elseif (strtotime($record['expires_at']) < time()) {
    // expired — ask the user to request a new one
} elseif ($record['attempts'] >= 5) {
    // too many attempts — block further tries on this record
} elseif (password_verify($enteredOtp, $record['otp_hash'])) {
    $pdo->prepare("UPDATE otp_verifications SET verified = 1 WHERE id = ?")
        ->execute([$record['id']]);
    // proceed to log the user in
} else {
    $pdo->prepare("UPDATE otp_verifications SET attempts = attempts + 1 WHERE id = ?")
        ->execute([$record['id']]);
    // incorrect code
}

5. Resend with a cooldown

$stmt = $pdo->prepare(
    "SELECT created_at FROM otp_verifications WHERE phone = ? ORDER BY id DESC LIMIT 1"
);
$stmt->execute([$phone]);
$last = $stmt->fetchColumn();

if ($last && (time() - strtotime($last)) < 60) {
    // reject — must wait before resending
}
// otherwise, repeat steps 1-3 to issue a new OTP

Laravel implementation

In Laravel, I split this into a controller, a service class, a model, and a queued job — never one fat controller method. Here's the architecture, not a full application:

Model

class OtpVerification extends Model
{
    protected $fillable = ['phone', 'otp_hash', 'expires_at', 'attempts', 'verified'];
    protected $casts = ['expires_at' => 'datetime', 'verified' => 'boolean'];
}

Service class

class WhatsAppOtpService
{
    public function generateAndSend(string $phone): void
    {
        $otp = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);

        OtpVerification::create([
            'phone'      => $phone,
            'otp_hash'   => Hash::make($otp),
            'expires_at' => now()->addMinutes(5),
        ]);

        SendWhatsAppOtpJob::dispatch($phone, $otp);
    }

    public function verify(string $phone, string $code): bool
    {
        $record = OtpVerification::where('phone', $phone)
            ->where('verified', false)
            ->latest('id')
            ->first();

        if (!$record || $record->expires_at->isPast() || $record->attempts >= 5) {
            return false;
        }

        if (Hash::check($code, $record->otp_hash)) {
            $record->update(['verified' => true]);
            return true;
        }

        $record->increment('attempts');
        return false;
    }
}

Queued job

Sending the WhatsApp message goes through a queue, not inline in the request, so a slow Meta API response never delays the user's page load:

class SendWhatsAppOtpJob implements ShouldQueue
{
    use Queueable, Dispatchable, InteractsWithQueue, SerializesModels;

    public function __construct(protected string $phone, protected string $otp) {}

    public function handle(WhatsAppClient $client): void
    {
        $client->sendTemplate($this->phone, 'otp_login', [$this->otp]);
    }
}

Controller, routes, validation, and middleware

// routes/web.php
Route::post('/otp/send', [OtpController::class, 'send'])
    ->middleware('throttle:5,1'); // 5 requests per minute per IP

Route::post('/otp/verify', [OtpController::class, 'verify'])
    ->middleware('throttle:10,1');
class OtpController extends Controller
{
    public function send(SendOtpRequest $request, WhatsAppOtpService $service)
    {
        $service->generateAndSend($request->validated('phone'));
        return response()->json(['message' => 'OTP sent']);
    }

    public function verify(VerifyOtpRequest $request, WhatsAppOtpService $service)
    {
        if (!$service->verify($request->validated('phone'), $request->validated('code'))) {
            return response()->json(['message' => 'Invalid or expired code'], 422);
        }

        // log the user in / regenerate session here
        return response()->json(['message' => 'Verified']);
    }
}

Validation lives in dedicated FormRequest classes (SendOtpRequest, VerifyOtpRequest) so phone format and code format checks never leak into the controller itself. Laravel's built-in throttle middleware handles rate limiting at the route level, on top of the attempt limit stored in the database.

Security best practices

  • Hash the OTP. Never store the raw 6-digit code — use password_hash()/Hash::make() exactly as you would for a password.
  • Five-minute expiry. Long enough for a real user, short enough to limit exposure.
  • Maximum retry attempts. Lock a record after 5 failed checks rather than allowing unlimited guesses.
  • Rate limiting on send. Cap how many OTP requests a single phone number or IP can trigger per minute, independent of the attempt limit on verification.
  • Replay protection. Mark a code as verified immediately on success so it can never be reused.
  • IP throttling. Combine phone-based and IP-based limits — a phone-only limit can still be abused across many numbers from one attacker.
  • Phone number validation. Validate format and country code server-side before ever calling Meta's API, to avoid wasting send quota on malformed numbers.
  • Audit logging. Log every send and verify event (without logging the plain OTP) for later investigation if abuse is suspected.

Production deployment tips

For shared hosting deployments — which is where a lot of my client work runs — a few things matter more than they would on a dedicated server:

  • Confirm cURL and openssl are enabled in your PHP configuration; most shared hosts have this by default, but it's worth checking on new accounts.
  • Your webhook URL must be served over HTTPS with a valid certificate — Meta will not send events to plain HTTP or self-signed certificates.
  • Store your permanent access token in an environment variable or .env file, never hardcoded in a committed file.
  • If you don't have a queue worker available on shared hosting, sending the WhatsApp API call synchronously is acceptable at low volume — just set a short cURL timeout (3-5 seconds) so a slow Meta response doesn't hang the whole request.

Common Meta errors and troubleshooting

ErrorLikely cause
Error 131026 — Message undeliverableRecipient's WhatsApp is inactive, or the number is not on WhatsApp at all
Error 132000 — Template does not existTemplate name/language code in your API call doesn't match an approved template exactly
Error 190 — Invalid access tokenYou're using a 24-hour temporary token instead of a System User's permanent token, or the token was revoked
Error 100 — Invalid parameterPhone number not in E.164 format, or a missing required template parameter
Webhook not receiving eventsVerification handshake wasn't completed correctly, or the URL isn't reachable over HTTPS from Meta's servers

Real project experience

A few lessons that only became obvious after actually running this in production, not from reading Meta's documentation:

Template approval delays are unpredictable. While implementing this for one client, an authentication template sat in review for over 48 hours with no clear reason, then approved without any changes on our end. I now build in a buffer of at least 2-3 days before a planned launch, specifically for template approval.

Permanent token confusion is the single most common setup mistake I've seen — including in my own early integrations. The quick-start page in Meta for Developers hands you a token that looks permanent but expires in 24 hours, and it's easy to miss that distinction until production breaks the next day.

Webhook verification failures are almost always a subtle mismatch between the verify token in your code and the one entered in the Meta dashboard — a trailing space or case difference is enough to fail the handshake silently.

Testing against your own number before going live catches most issues early, but I always test with at least one number that has never messaged the business number before, since first-contact delivery sometimes behaves differently than delivery to an existing conversation thread.

Cost comparison

PlatformPlatform feeMonthly feeFlexibilityDeveloper controlVendor lock-inBest use case
Meta Cloud API (direct)NoneNoneFullFullNoneTeams with an in-house or freelance developer
TwilioPer-message markupSometimesHighModerateLowMulti-channel messaging beyond just WhatsApp
MSG91Per-message markupSometimesModerateModerateModerateTeams already using MSG91 for SMS
InteraktPer-message markupYesModerateLowHigherNon-technical teams wanting a full CRM-style dashboard
AiSensyPer-message markupYesModerateLowHigherMarketing-led teams needing broadcast + support tools
360dialogSmall markupSometimesHighHighLowDevelopers wanting BSP support without a heavy dashboard layer

None of these providers are a bad choice — they solve a real problem for teams that don't want to manage Meta Business Settings, template submissions, and webhook infrastructure themselves. The trade-off is simply cost versus convenience, and that trade-off shifts as your message volume grows.

Real client examples

Startup · WhatsApp OTP

Early-stage SaaS signup flow

The problem

A subscription-based SaaS startup was paying a third-party OTP provider a fixed monthly platform fee plus a per-message rate, which didn't make sense at their early signup volume of a few hundred verifications a month.

What I did

Migrated their signup verification directly onto Meta Cloud API, built the OTP generation, hashing, and verification logic in Laravel, and set up a queued job so OTP sending never blocked the signup request.

The result

Removed the monthly platform fee entirely and reduced their per-verification cost, while keeping the same 5-minute expiry and attempt-limit security they had with the previous provider.

eCommerce · Checkout OTP

Custom PHP eCommerce checkout

The problem

An online store wanted to verify phone numbers at checkout to reduce fake orders and cash-on-delivery abandonment, but was hesitant to add a costly third-party OTP subscription to an already thin margin business.

What I did

Implemented WhatsApp OTP verification directly at the checkout phone number field using Meta Cloud API, with a fallback to SMS OTP for numbers where WhatsApp delivery failed, using the same otp_verifications table for both channels.

The result

Fake and abandoned cash-on-delivery orders dropped noticeably after launch, with no added per-transaction platform fee beyond the raw Meta messaging cost.

CRM · Client Login Portal

Custom CRM client login portal

The problem

A CRM used by field staff and external clients needed passwordless login for non-technical users, but the client wanted to avoid recurring per-seat OTP provider costs as their user base grew.

What I did

Built a WhatsApp OTP-based passwordless login using Meta Cloud API and a permanent system-user token, with rate limiting on both the send and verify endpoints and audit logging of every OTP event for compliance.

The result

Login friction dropped since users no longer needed to remember passwords, and the OTP cost stayed proportional to actual logins rather than a fixed per-seat license.

Closing thoughts

WhatsApp OTP login through Meta Cloud API isn't more complicated than going through a third-party provider — it's roughly the same amount of setup work, just with the accounts and configuration living directly under your own Meta Business Account instead of a reseller's dashboard. What you get in exchange is full control over cost, no recurring platform fee, and no dependency on a provider's uptime layered on top of Meta's own.

If you already have a developer capable of writing the PHP or Laravel side of this, the case for going direct is straightforward. If you don't, that's exactly the kind of project I take on.

Frequently asked questions

What is WhatsApp OTP login?
WhatsApp OTP login is a verification method where a one-time password is sent to a user's WhatsApp number instead of by SMS or email, and the user enters that code on your website to confirm their identity or complete a login.
Is WhatsApp OTP cheaper than SMS OTP?
In most cases, yes. WhatsApp Business API authentication conversations are typically priced lower than SMS in markets like India, and delivery rates are usually higher since WhatsApp messages rarely get filtered the way SMS can be by carriers.
Do I need a third-party provider to send WhatsApp OTPs?
No. You can integrate directly with Meta's Cloud API using your own Meta Business Account and a verified phone number, without paying a third-party platform fee on top of Meta's own messaging cost.
What is Meta Cloud API?
Meta Cloud API is Meta's own hosted version of the WhatsApp Business Platform API, letting developers send and receive WhatsApp messages directly through Meta's servers without needing to host the older on-premises API.
How much does Meta Cloud API cost for OTP messages?
Meta charges per authentication conversation, billed per 24-hour conversation window, at rates that vary by country. There is no monthly platform fee from Meta itself — you only pay for what you send, on top of your own hosting cost.
What is a WhatsApp Authentication Template?
It's a pre-approved message format specifically for one-time passwords and verification codes. Meta requires OTP messages to use this template category, and templates must be submitted and approved before they can be used in production.
How long does WhatsApp template approval take?
In my projects, straightforward authentication templates are usually approved within a few hours to one business day. Approval times can extend to several days if the template wording doesn't clearly match Meta's authentication template guidelines.
Can I use my personal WhatsApp number for OTP sending?
No. You need a business phone number registered specifically to the WhatsApp Business Platform, verified through your Meta Business Account, and that number cannot simultaneously be used in the regular consumer WhatsApp or Business apps.
What is a permanent access token in Meta Cloud API?
It's a long-lived authentication token generated through a System User in Meta Business Settings, used to authorize your server's API calls without needing to refresh short-lived tokens every 24 hours.
What is webhook verification for WhatsApp Cloud API?
It's a handshake step where Meta sends a GET request with a verification token to your webhook URL, and your server must echo back a challenge value correctly before Meta will start sending delivery and status events to that URL.
Should I store the OTP in plain text in my database?
No. Always hash the OTP before storing it, the same way you would hash a password, using password_hash() in PHP. Verify user input with password_verify() rather than a plain string comparison.
How long should a WhatsApp OTP remain valid?
Five minutes is the industry standard and what I use in production. Long enough for users to switch apps and read the code, short enough to limit the window for abuse if a code is somehow intercepted.
How do I prevent OTP brute-force attacks?
Limit each OTP record to a fixed number of verification attempts, typically five, and lock further attempts once that limit is hit. Combine this with rate limiting on the send endpoint so an attacker can't request unlimited new codes either.
What is OTP replay protection?
It means an OTP can only be used successfully once. As soon as a code is verified, mark that database row as used so the same code can't be resubmitted later even if it's still within its expiry window.
Can I send WhatsApp OTPs from shared hosting?
Yes. The Cloud API calls are plain HTTPS requests, so any PHP environment with cURL enabled, including GoDaddy or similar cPanel shared hosting, can send them. The only requirement is that your webhook URL is publicly reachable over HTTPS.
What is the difference between Meta Cloud API and the WhatsApp Business App?
The Business App is a consumer-facing app for small businesses to chat manually. Cloud API is a developer-facing API meant for programmatic, automated messaging at scale, and the two cannot share the same phone number.
Why did my WhatsApp template get rejected?
The most common reasons in my experience are including promotional language inside an authentication template, missing the required OTP placeholder formatting, or submitting wording that resembles a marketing template instead of a strict verification message.
Can I use WhatsApp OTP with Laravel?
Yes. Laravel's HTTP client and queue system are a natural fit for calling Meta Cloud API asynchronously, and Laravel's built-in rate limiting middleware pairs well with OTP send endpoints.
What happens if a user's WhatsApp is not installed?
The message delivery will fail, and Meta's webhook will send a failed status event for that message. It's good practice to fall back to SMS OTP for numbers where WhatsApp delivery fails.
Is WhatsApp OTP login secure enough for financial applications?
WhatsApp OTP provides comparable security to SMS OTP when implemented correctly with hashing, expiry, and attempt limits. For high-value financial transactions, combining it with a second factor is still recommended, same as with SMS.
What is the otp_verifications table used for?
It's the database table that tracks every OTP that has been generated, including its hashed value, expiry time, number of verification attempts, and whether it has already been successfully used.
How do I resend an OTP without spamming the user?
Add a minimum cooldown, typically 30 to 60 seconds, before a new OTP can be requested for the same phone number, and disable the resend button on the frontend until that cooldown expires.
Which is cheaper long-term: Meta Cloud API directly, or a provider like Twilio or MSG91?
Direct Meta Cloud API integration is almost always cheaper at any meaningful volume, since you remove the provider's markup entirely. Providers earn their fee back through faster setup, dashboards, and support, which matters more for very low-volume or non-technical teams.
Do I need a WhatsApp Business Manager verification (Meta Verified) to send OTPs?
Full Meta Business verification isn't always required to start sending, but it increases your messaging limits significantly and is required if you want to scale beyond the initial tier limits Meta applies to new accounts.
Can I migrate from a third-party OTP provider to direct Meta Cloud API later?
Yes, and it's a common request I get. Since the underlying WhatsApp Business Account can often be reused, migration mainly involves generating a new permanent token, re-pointing the webhook, and re-approving your authentication template under direct API access.
Ready When You Are

Want WhatsApp OTP built directly on Meta Cloud API?

PHP, Laravel, OpenCart, WordPress — send me your project on WhatsApp for a free initial consultation.

Message Me on WhatsApp