DEV Community

VirtualSMS
VirtualSMS

Posted on

How to Add SMS Verification to Your App with Real SIM Cards

How to Add SMS Verification to Your App with Real SIM Cards

If you're building an app that requires phone verification, you've probably considered Twilio or similar VoIP providers. They work fine for sending SMS — but they fail badly when you need to receive verification codes from services like WhatsApp, Telegram, or banking apps.

Why? Because those services detect and reject VoIP numbers.

In this tutorial, I'll show you how to integrate SMS verification using real physical SIM cards through the VirtualSMS API — and why it matters for QA testing, multi-account management, and automation.

Why Real SIM Cards?

Most SMS APIs give you a virtual number. These numbers:

  • Get blocked by WhatsApp (90%+ rejection rate)
  • Get flagged by Telegram, Google, Instagram
  • Fail on banking/crypto exchange verification
  • Leave audit trails that link back to VoIP providers

Physical SIM numbers sit in actual hardware modems on real cellular networks. To any service checking the number type, they're indistinguishable from a personal phone.

The VirtualSMS API

VirtualSMS offers two API styles:

Style Endpoint Best For
sms-activate Compatible /stubs/handler_api.php Migrating from DaisySMS, existing bots
Modern REST /api/v1/ New integrations, webhooks, WebSocket

Both require an API key from your VirtualSMS dashboard (Settings → API Keys).

Full API docs: virtualsms.io/api

Quick Start: Get a Number and Receive an SMS

Using curl (sms-activate compatible API)

Step 1: Check your balance

curl "https://virtualsms.io/stubs/handler_api.php?action=getBalance&api_key=YOUR_API_KEY"
# Response: ACCESS_BALANCE:50.30
Enter fullscreen mode Exit fullscreen mode

Step 2: Get a number for WhatsApp

curl "https://virtualsms.io/stubs/handler_api.php?action=getNumber&service=wa&country=187&api_key=YOUR_API_KEY"
# Response: ACCESS_NUMBER:12345:13476711222
# Format: ACCESS_NUMBER:{activationId}:{phoneNumber}
Enter fullscreen mode Exit fullscreen mode

Step 3: Poll for the SMS code

curl "https://virtualsms.io/stubs/handler_api.php?action=getStatus&id=12345&api_key=YOUR_API_KEY"
# Waiting: STATUS_WAIT_CODE
# Received: STATUS_OK:438271
Enter fullscreen mode Exit fullscreen mode

Step 4: Mark as done

curl "https://virtualsms.io/stubs/handler_api.php?action=setStatus&id=12345&status=6&api_key=YOUR_API_KEY"
# Response: ACCESS_ACTIVATION
Enter fullscreen mode Exit fullscreen mode

Using Node.js (Modern REST API)

const API_KEY = 'vsms_your_api_key_here';
const BASE = 'https://virtualsms.io/api/v1';

const headers = { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' };

// Step 1: Rent a number for WhatsApp
const rentResponse = await fetch(`${BASE}/numbers/rent`, {
  method: 'POST',
  headers,
  body: JSON.stringify({ service: 'whatsapp', country: 'US' })
});
const rental = await rentResponse.json();
console.log(`Got number: ${rental.phone} (rental ID: ${rental.rental_id})`);

// Step 2: Poll for SMS (or use webhooks — see below)
const poll = async (rentalId) => {
  while (true) {
    const res = await fetch(`${BASE}/numbers/${rentalId}/status`, { headers });
    const data = await res.json();

    if (data.status === 'received') {
      console.log(`SMS code: ${data.sms_code}`);
      console.log(`Full text: ${data.text}`);
      return data.sms_code;
    }

    // Wait 5 seconds before next check
    await new Promise(r => setTimeout(r, 5000));
  }
};

const code = await poll(rental.rental_id);

// Step 3: Mark as done
await fetch(`${BASE}/numbers/${rental.rental_id}/done`, {
  method: 'POST',
  headers
});
Enter fullscreen mode Exit fullscreen mode

Using Webhooks (No Polling Needed)

Instead of polling every few seconds, register a webhook URL in your dashboard. When an SMS arrives, VirtualSMS sends a POST to your endpoint:

{
  "activationId": 12345,
  "service": "wa",
  "code": "438271",
  "text": "Your WhatsApp code is 438271. Don't share this code.",
  "country": 187,
  "receivedAt": "2026-03-03 14:22:11"
}
Enter fullscreen mode Exit fullscreen mode

This is much more efficient for production systems — zero delay, zero wasted API calls.

Migrating from DaisySMS

DaisySMS shut down in December 2025. If you have existing automation built on their API, VirtualSMS is a drop-in replacement. The API is fully compatible — same action names, same response format, same error codes. One line change:

- const BASE_URL = "https://daisysms.com/stubs/handler_api.php"
+ const BASE_URL = "https://virtualsms.io/stubs/handler_api.php"
Enter fullscreen mode Exit fullscreen mode

No other code changes required.

Use Cases for Developers

  • QA/Testing: Verify your app's phone verification flow without using personal numbers
  • Multi-account testing: Test how your service handles multiple registrations
  • CI/CD pipelines: Automated end-to-end tests that include SMS verification steps
  • Service monitoring: Check that verification flows work across different countries

Pricing

Activations start at $0.15 per SMS. Rentals (dedicated numbers for 1-90 days) start at $3/day. Full pricing breakdown here.

Payments accepted via cryptocurrency — no credit card needed.


VirtualSMS Resources:

Top comments (0)