<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: VirtualSMS</title>
    <description>The latest articles on Forem by VirtualSMS (@virtualsms).</description>
    <link>https://forem.com/virtualsms</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3878436%2F44e09e74-7efc-4cc2-a8ba-d00968527a17.png</url>
      <title>Forem: VirtualSMS</title>
      <link>https://forem.com/virtualsms</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/virtualsms"/>
    <language>en</language>
    <item>
      <title>x402 SMS Verification: Pay-Per-Call OTP for Autonomous Agents</title>
      <dc:creator>VirtualSMS</dc:creator>
      <pubDate>Sat, 02 May 2026 08:43:23 +0000</pubDate>
      <link>https://forem.com/virtualsms/x402-sms-verification-pay-per-call-otp-for-autonomous-agents-16pd</link>
      <guid>https://forem.com/virtualsms/x402-sms-verification-pay-per-call-otp-for-autonomous-agents-16pd</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;AI agents need to spend money. Operators don't want to manage API keys for every agent. &lt;strong&gt;HTTP 402 Payment Required&lt;/strong&gt; + &lt;strong&gt;Coinbase x402 facilitator&lt;/strong&gt; = agent pays once, gets &lt;code&gt;api_key&lt;/code&gt; + balance, spends down on real services. Here's how I wired it on our SMS verification API, with working Python code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is x402?
&lt;/h2&gt;

&lt;p&gt;HTTP 402 has been a "reserved for future use" status code in the spec since 1997. In 2025, Coinbase (with Cloudflare and others) built the &lt;a href="https://www.x402.org" rel="noopener noreferrer"&gt;x402 protocol&lt;/a&gt; on top of it — a standard way to settle USDC micropayments inline with HTTP requests.&lt;/p&gt;

&lt;p&gt;The flow is dead simple from an agent's perspective:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Agent makes a normal POST to your endpoint&lt;/li&gt;
&lt;li&gt;Server returns &lt;code&gt;402 Payment Required&lt;/code&gt; with a JSON manifest listing accepted networks (Base / Solana / Polygon), token (USDC / USDT), amount, and recipient address&lt;/li&gt;
&lt;li&gt;Agent (using &lt;code&gt;x402-fetch&lt;/code&gt; or a similar SDK) signs an EIP-3009 authorization, retries with the &lt;code&gt;X-PAYMENT&lt;/code&gt; header&lt;/li&gt;
&lt;li&gt;Server verifies via the Coinbase facilitator, settles on-chain, returns &lt;code&gt;200 OK&lt;/code&gt; with whatever the agent paid for&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No API keys exchanged out-of-band. No paid tier flow. No human in the loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why deposit-first instead of per-call?
&lt;/h2&gt;

&lt;p&gt;The "obvious" x402 design is: every API call returns 402, agent pays per request. We tried it. It doesn't work for variable-priced services.&lt;/p&gt;

&lt;p&gt;SMS verification ranges from $0.05 (cheap-country Discord) to $5 (premium WhatsApp on a high-block carrier). If x402 charges a flat $0.10 per call, you either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lose money&lt;/strong&gt; on the $5 service (you charged $0.10, paid the carrier $5)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rip off the agent&lt;/strong&gt; on the $0.05 service (you charged $0.10, retail is $0.05)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Per-call flat-fee only works for fixed-cost services. For variable-priced retail, you need &lt;strong&gt;deposit-first&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Agent deposits $X via x402 (one 402 → settle → 200 cycle)&lt;/li&gt;
&lt;li&gt;Agent gets &lt;code&gt;api_key&lt;/code&gt; + &lt;code&gt;balance&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Agent uses &lt;code&gt;api_key&lt;/code&gt; on the normal REST endpoints, debited at retail prices&lt;/li&gt;
&lt;li&gt;Refunds (when SMS doesn't arrive within timeout) credit back to the &lt;code&gt;api_key&lt;/code&gt; balance&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Same protocol surface from the agent's perspective (one 402 dance), real retail pricing on the back end.&lt;/p&gt;

&lt;h2&gt;
  
  
  The endpoint
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;POST /api/v1/x402/topup&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;An empty body returns the manifest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"x402Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"accepts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"scheme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USDC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"maxAmountRequired"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5000000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://virtualsms.io/api/v1/x402/topup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Top up VirtualSMS API balance — pay $5.00 USDC on base, receive API key + $5.00 balance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"payTo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0xfEc54264350d97d9b63f9Cc415BAF708C4695F32"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"maxTimeoutSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"asset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"extra"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD Coin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Payment required"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three networks accepted: Base USDC, Solana USDC, Solana USDT. $2 minimum deposit, $5 default, $10,000 max.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working agent code
&lt;/h2&gt;

&lt;p&gt;Using &lt;code&gt;x402-fetch&lt;/code&gt; (Coinbase's reference SDK):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;x402&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wrap_fetch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;web3&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;

&lt;span class="c1"&gt;# Agent's own wallet (separate from the recipient address)
&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AGENT_PRIVATE_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wrap_fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 1. Deposit — single 402 -&amp;gt; settle -&amp;gt; 200 cycle
&lt;/span&gt;&lt;span class="n"&gt;deposit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://virtualsms.io/api/v1/x402/topup&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;balance_usd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Got api_key with $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; balance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Buy a number — normal REST, no x402 needed
&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://virtualsms.io/api/v1/customer/purchase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-API-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;discord&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;country&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;phone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;phone_number&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Got number: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (order: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 3. Poll for SMS
&lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://virtualsms.io/api/v1/customer/order/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-API-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sms_received&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OTP: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sms_text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What the API key buys
&lt;/h2&gt;

&lt;p&gt;Once an agent has the &lt;code&gt;api_key&lt;/code&gt; + balance, the rest of the API is pure REST:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /api/v1/catalog/services&lt;/code&gt; — full service catalog (~2,500 services)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/v1/catalog/countries&lt;/code&gt; — 145+ countries with live availability&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /api/v1/customer/purchase&lt;/code&gt; — buy a number for a service+country&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/v1/customer/order/{id}&lt;/code&gt; — poll for SMS arrival&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /api/v1/customer/cancel/{id}&lt;/code&gt; — cancel + refund (within the 120s cooldown)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Auth header is &lt;code&gt;X-API-Key&lt;/code&gt;. The cooldown between cancel and re-purchase is 120 seconds, server-side.&lt;/p&gt;

&lt;h2&gt;
  
  
  What surprised me building this
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Self-payment works on Base.&lt;/strong&gt; Coinbase's facilitator allows &lt;code&gt;payer == recipient&lt;/code&gt; settlements. Useful for ops smoke tests where you don't want to spin up a separate test wallet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The manifest needs &lt;code&gt;extra: { name, version }&lt;/code&gt;&lt;/strong&gt; for the EIP-712 domain to validate cleanly across all clients. We caught this mid-test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solana token-account initialization matters&lt;/strong&gt; — wallets without a USDC token account just fail silently. Worth noting in the manifest description.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Variable timeouts per supplier&lt;/strong&gt; — some services activate in 30 seconds, others in 20 minutes. The agent should poll on a backoff curve, not a tight loop.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why this matters for AI agents
&lt;/h2&gt;

&lt;p&gt;The whole point of x402 is removing the human from the payment loop. An autonomous agent doing account signups, OTP receipts, or workflow automation can't pause to email an operator for an &lt;code&gt;api_key&lt;/code&gt;. With deposit-first x402:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent budgets $50 in USDC at the start of a workflow&lt;/li&gt;
&lt;li&gt;Agent does whatever — verify accounts, rent numbers, pull OTPs&lt;/li&gt;
&lt;li&gt;Agent never asks for credentials or keys&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the same idea Coinbase had when shipping x402 in the first place: HTTP at scale, but agentic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If you're building agent workflows that need SMS verification, the endpoint is live. The manifest is real, settlement works, the code above is exact. Hit &lt;code&gt;/api/v1/x402/info&lt;/code&gt; for the current state of accepted networks and price floors.&lt;/p&gt;

&lt;p&gt;Full API reference: &lt;a href="https://virtualsms.io/api" rel="noopener noreferrer"&gt;virtualsms.io/api&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; I operate &lt;a href="https://virtualsms.io" rel="noopener noreferrer"&gt;virtualsms.io&lt;/a&gt; and built this implementation. Happy to answer questions about the protocol or the SMS verification side in the comments.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Source code &amp;amp; install:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/virtualsms-io/mcp-server" rel="noopener noreferrer"&gt;GitHub — virtualsms-io/mcp-server&lt;/a&gt; — open-source MCP server&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/virtualsms-mcp" rel="noopener noreferrer"&gt;npm — virtualsms-mcp&lt;/a&gt; — &lt;code&gt;npx virtualsms-mcp&lt;/code&gt; to run locally&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mcp.virtualsms.io/mcp" rel="noopener noreferrer"&gt;Hosted MCP endpoint&lt;/a&gt; — StreamableHTTP, zero install&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://virtualsms.io/api" rel="noopener noreferrer"&gt;API reference&lt;/a&gt; — full REST + sms-activate compatible API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Listed in:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://registry.modelcontextprotocol.io/v0/servers?search=virtualsms" rel="noopener noreferrer"&gt;Model Context Protocol Registry (official)&lt;/a&gt; — Anthropic-maintained&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://smithery.ai/servers/@virtualsms/virtualsms-mcp" rel="noopener noreferrer"&gt;Smithery (MCP)&lt;/a&gt; — quality score 98/100&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://smithery.ai/skills/virtualsms/sms-verification" rel="noopener noreferrer"&gt;Smithery (Claude Skill)&lt;/a&gt; — Skill format, 20+ agent clients&lt;/li&gt;
&lt;li&gt;&lt;a href="https://glama.ai/mcp/servers/virtualsms-io/mcp-server" rel="noopener noreferrer"&gt;Glama&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mcp.so/server/virtualsms/virtualsms-io" rel="noopener noreferrer"&gt;mcp.so&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lobehub.com/mcp/virtualsms-io-mcp-server" rel="noopener noreferrer"&gt;Lobehub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://clawhub.ai/virtualsms/virtualsms-sms-verification" rel="noopener noreferrer"&gt;ClawHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://alternativeto.net/software/virtualsms/" rel="noopener noreferrer"&gt;AlternativeTo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cryptwerk.com/company/virtualsms/" rel="noopener noreferrer"&gt;Cryptwerk (crypto-payments directory)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Related read:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/virtualsms/how-to-add-sms-verification-to-your-app-with-real-sim-cards-50lc"&gt;How to Add SMS Verification to Your App with Real SIM Cards&lt;/a&gt; — companion dev.to post covering the sms-activate-compatible API + Node.js examples&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>blockchain</category>
      <category>python</category>
    </item>
    <item>
      <title>How to Add SMS Verification to Your App with Real SIM Cards</title>
      <dc:creator>VirtualSMS</dc:creator>
      <pubDate>Tue, 14 Apr 2026 11:02:41 +0000</pubDate>
      <link>https://forem.com/virtualsms/how-to-add-sms-verification-to-your-app-with-real-sim-cards-50lc</link>
      <guid>https://forem.com/virtualsms/how-to-add-sms-verification-to-your-app-with-real-sim-cards-50lc</guid>
      <description>&lt;h1&gt;
  
  
  How to Add SMS Verification to Your App with Real SIM Cards
&lt;/h1&gt;

&lt;p&gt;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 &lt;em&gt;receive&lt;/em&gt; verification codes from services like WhatsApp, Telegram, or banking apps.&lt;/p&gt;

&lt;p&gt;Why? Because those services detect and reject VoIP numbers.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Why Real SIM Cards?
&lt;/h2&gt;

&lt;p&gt;Most SMS APIs give you a virtual number. These numbers:&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  The VirtualSMS API
&lt;/h2&gt;

&lt;p&gt;VirtualSMS offers two API styles:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Style&lt;/th&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;sms-activate Compatible&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/stubs/handler_api.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Migrating from DaisySMS, existing bots&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Modern REST&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/api/v1/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New integrations, webhooks, WebSocket&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both require an API key from your &lt;a href="https://virtualsms.io" rel="noopener noreferrer"&gt;VirtualSMS dashboard&lt;/a&gt; (Settings → API Keys).&lt;/p&gt;

&lt;p&gt;Full API docs: &lt;a href="https://virtualsms.io/api" rel="noopener noreferrer"&gt;virtualsms.io/api&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Start: Get a Number and Receive an SMS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Using curl (sms-activate compatible API)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Check your balance&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://virtualsms.io/stubs/handler_api.php?action=getBalance&amp;amp;api_key=YOUR_API_KEY"&lt;/span&gt;
&lt;span class="c"&gt;# Response: ACCESS_BALANCE:50.30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Get a number for WhatsApp&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://virtualsms.io/stubs/handler_api.php?action=getNumber&amp;amp;service=wa&amp;amp;country=187&amp;amp;api_key=YOUR_API_KEY"&lt;/span&gt;
&lt;span class="c"&gt;# Response: ACCESS_NUMBER:12345:13476711222&lt;/span&gt;
&lt;span class="c"&gt;# Format: ACCESS_NUMBER:{activationId}:{phoneNumber}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Poll for the SMS code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://virtualsms.io/stubs/handler_api.php?action=getStatus&amp;amp;id=12345&amp;amp;api_key=YOUR_API_KEY"&lt;/span&gt;
&lt;span class="c"&gt;# Waiting: STATUS_WAIT_CODE&lt;/span&gt;
&lt;span class="c"&gt;# Received: STATUS_OK:438271&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Mark as done&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://virtualsms.io/stubs/handler_api.php?action=setStatus&amp;amp;id=12345&amp;amp;status=6&amp;amp;api_key=YOUR_API_KEY"&lt;/span&gt;
&lt;span class="c"&gt;# Response: ACCESS_ACTIVATION&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using Node.js (Modern REST API)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vsms_your_api_key_here&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://virtualsms.io/api/v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-API-Key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Step 1: Rent a number for WhatsApp&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rentResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/numbers/rent`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;whatsapp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rental&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rentResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Got number: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rental&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (rental ID: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rental&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rental_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Step 2: Poll for SMS (or use webhooks — see below)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;poll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rentalId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/numbers/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rentalId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/status`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;received&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`SMS code: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sms_code&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Full text: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sms_code&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Wait 5 seconds before next check&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;poll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rental&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rental_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Step 3: Mark as done&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/numbers/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rental&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rental_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/done`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Webhooks (No Polling Needed)
&lt;/h2&gt;

&lt;p&gt;Instead of polling every few seconds, register a webhook URL in your dashboard. When an SMS arrives, VirtualSMS sends a POST to your endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"activationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"438271"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your WhatsApp code is 438271. Don't share this code."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;187&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"receivedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-03 14:22:11"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much more efficient for production systems — zero delay, zero wasted API calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrating from DaisySMS
&lt;/h2&gt;

&lt;p&gt;DaisySMS shut down in December 2025. If you have existing automation built on their API, VirtualSMS is a &lt;a href="https://virtualsms.io/daisysms-alternative" rel="noopener noreferrer"&gt;drop-in replacement&lt;/a&gt;. The API is fully compatible — same action names, same response format, same error codes. One line change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- const BASE_URL = "https://daisysms.com/stubs/handler_api.php"
&lt;/span&gt;&lt;span class="gi"&gt;+ const BASE_URL = "https://virtualsms.io/stubs/handler_api.php"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No other code changes required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Cases for Developers
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;Activations start at $0.05 per SMS. Rentals (dedicated numbers for 1-90 days) start at $3/day. Full &lt;a href="https://virtualsms.io/pricing" rel="noopener noreferrer"&gt;pricing breakdown here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Payments accepted via cryptocurrency — no credit card needed.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;VirtualSMS Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Best SMS Verification Services 2026: &lt;a href="https://penzu.com/p/9d5622c72841a2aa" rel="noopener noreferrer"&gt;https://penzu.com/p/9d5622c72841a2aa&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Developer API Tutorial: &lt;a href="https://dev.to/virtualsms/how-to-add-sms-verification-to-your-app-with-real-sim-cards-50lc"&gt;https://dev.to/virtualsms/how-to-add-sms-verification-to-your-app-with-real-sim-cards-50lc&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Руководство на русском: &lt;a href="https://teletype.in/@virtualsms/NReaV38zcpW" rel="noopener noreferrer"&gt;https://teletype.in/@virtualsms/NReaV38zcpW&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Why VoIP Numbers Fail: &lt;a href="https://telegra.ph/Why-VoIP-Numbers-Fail-for-SMS-Verification-and-What-Actually-Works-in-2026-04-14" rel="noopener noreferrer"&gt;https://telegra.ph/Why-VoIP-Numbers-Fail-for-SMS-Verification-and-What-Actually-Works-in-2026-04-14&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DaisySMS Alternative Guide: &lt;a href="https://medium.com/@dev_13666/daisysms-shut-down-what-now-the-best-alternative-with-full-api-compatibility-2026-06e484d93d23" rel="noopener noreferrer"&gt;https://medium.com/@dev_13666/daisysms-shut-down-what-now-the-best-alternative-with-full-api-compatibility-2026-06e484d93d23&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;VirtualSMS on Grokipedia: &lt;a href="https://grokipedia.com/page/VirtualSMS" rel="noopener noreferrer"&gt;https://grokipedia.com/page/VirtualSMS&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;VirtualSMS on Cryptwerk: &lt;a href="https://cryptwerk.com/company/virtualsms/" rel="noopener noreferrer"&gt;https://cryptwerk.com/company/virtualsms/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;WhatsApp Verification Guide: &lt;a href="https://virtualsms9.wordpress.com/2026/04/14/how-to-get-a-virtual-phone-number-for-whatsapp-verification-2026-guide/" rel="noopener noreferrer"&gt;https://virtualsms9.wordpress.com/2026/04/14/how-to-get-a-virtual-phone-number-for-whatsapp-verification-2026-guide/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Anonymous SMS Verification: &lt;a href="https://virtualsmsio.blogspot.com/2026/04/protect-your-phone-number-anonymous-sms.html" rel="noopener noreferrer"&gt;https://virtualsmsio.blogspot.com/2026/04/protect-your-phone-number-anonymous-sms.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telegram Without Phone Number: &lt;a href="https://www.tumblr.com/virtualsms/813881309501014016/how-to-use-telegram-without-your-personal-phone" rel="noopener noreferrer"&gt;https://www.tumblr.com/virtualsms/813881309501014016/how-to-use-telegram-without-your-personal-phone&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;VirtualSMS Guide PDF: &lt;a href="https://issuu.com/virtualsms/docs/ms_verification_with_real_sim_cards_virtualsms_g" rel="noopener noreferrer"&gt;https://issuu.com/virtualsms/docs/ms_verification_with_real_sim_cards_virtualsms_g&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;VirtualSMS on AlternativeTo: &lt;a href="https://alternativeto.net/software/virtualsms/" rel="noopener noreferrer"&gt;https://alternativeto.net/software/virtualsms/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>sms</category>
      <category>api</category>
      <category>webdev</category>
      <category>verification</category>
    </item>
  </channel>
</rss>
