<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>SMSTunnel Blog</title>
    <link>https://smstunnel.io/blog/</link>
    <description>Guides for sending texts from your own software through your own Android phone and number.</description>
    <language>en</language>
    <lastBuildDate>Mon, 09 Nov 2026 09:00:00 GMT</lastBuildDate>
    <atom:link href="https://smstunnel.io/blog/rss.xml" rel="self" type="application/rss+xml" />
    <image>
      <url>https://smstunnel.io/android-chrome-192x192.png</url>
      <title>SMSTunnel Blog</title>
      <link>https://smstunnel.io/blog/</link>
    </image>
    <item>
      <title>The SMSTunnel desktop app is here: your texts on your computer</title>
      <link>https://smstunnel.io/blog/desktop-app/</link>
      <guid isPermaLink="true">https://smstunnel.io/blog/desktop-app/</guid>
      <pubDate>Tue, 03 Nov 2026 09:30:00 GMT</pubDate>
      <dc:creator>NARBOWEB SRL</dc:creator>
      <category>Desktop app</category>
      <category>Windows</category>
      <category>Linux</category>
      <category>Privacy</category>
      <description>The free SMSTunnel app for Windows and Linux: write and read your texts on your computer, and your own Android phone sends them from your own number.</description>
      <content:encoded><![CDATA[<p>SMSTunnel for Windows and Linux is out, and it’s free. It brings your texts to your computer: you read and write them on a real keyboard, and your own Android phone sends each one from your own number. With sync on in the phone app, the texts your phone receives show up on your computer too, each with a notification.</p>
<p>Until now, you wrote texts on your computer in the <a href="https://dashboard.smstunnel.io/">dashboard</a>, in a browser tab. The desktop app does the same job as a real app: it stays in the tray, it can start with your computer, and it tells you when a text arrives.</p>
<p><img src="https://smstunnel.io/desktop/screenshot-en-light.webp" alt="The SMSTunnel desktop app: conversations on the left, with your phones and whether they&#x27;re connected below them, and a conversation with sent and received texts on the right" loading="lazy" decoding="async" width="1280" height="800"></p>
<h2 id="what-the-desktop-app-does">What the desktop app does</h2>
<ul>
<li><strong>Your conversations, by number.</strong> With search, unread markers and each text’s status: sent, delivered or failed.</li>
<li><strong>Writing through your own phone.</strong> You type on your computer, and your phone sends the text from your number, so people see a number they know and can reply. One text goes to one number.</li>
<li><strong>Notifications.</strong> With sync on in the phone app, every text your phone receives shows up as a notification, with its text or just “New text”.</li>
<li><strong>The tray.</strong> Close the window and SMSTunnel keeps running, so new texts still show up. It can start with your computer.</li>
<li><strong>All your phones.</strong> See which ones are connected and choose which one sends. On a dual-SIM phone, a reply goes out from the SIM that received the text.</li>
<li><strong>Updates.</strong> It updates itself, and installs only updates signed by SMSTunnel.</li>
<li><strong>Light or dark, English or Romanian,</strong> like your computer.</li>
</ul>
<h2 id="how-to-start">How to start</h2>
<ol>
<li>Install the SMSTunnel app on your Android phone from <a href="https://play.google.com/store/apps/details?id=io.smstunnel.app">Google Play</a> and sign in with your SMSTunnel account. Keep the phone online, with the app running: the phone is what sends your texts.</li>
<li><a href="https://smstunnel.io/desktop/">Download the desktop app</a> for Windows (10 or 11, 64-bit) or Linux (AppImage, .deb or .rpm), and install it.</li>
<li>Open it and sign in with the same SMSTunnel account. Your phone shows up by itself.</li>
<li>To see the texts your phone receives, turn on <strong>Settings › Received texts › Sync received texts</strong> in the phone app.</li>
</ol>
<p>If you sign in with Google or Facebook, first set a password in the dashboard, under Account › Security, then sign in on your computer with your email.</p>
<p>On Windows, the simplest way is the <a href="https://apps.microsoft.com/detail/9n4bmhrw6t7s">Microsoft Store</a>: it installs SMSTunnel without any warning and keeps it up to date. With the installer from the download page instead, the first time you open it Windows may say “Windows protected your PC”: that installer isn’t signed with a code-signing certificate yet. Click <strong>More info</strong>, then <strong>Run anyway</strong>. <a href="https://smstunnel.io/desktop/#windows">The download page</a> has the details, and the checksums to check your download.</p>
<h2 id="your-texts-and-your-privacy">Your texts and your privacy</h2>
<p>This is how texts travel today, in the desktop app as in the dashboard:</p>
<ul>
<li><strong>Texts you write</strong> go through your SMSTunnel account to your phone, which sends them.</li>
<li><strong>Texts your phone receives</strong> reach your computer only while sync is on in the phone app. Only texts that arrive after you turn it on are synced, and your existing inbox is never read.</li>
<li><strong>In your account,</strong> your texts are stored in readable form, so the dashboard and the desktop app can show them. They aren’t end-to-end encrypted.</li>
<li><strong>One exception:</strong> the lock next to the text box, “Encrypt for my phone”, encrypts a text on your computer so that only your phone can read it. smstunnel.io still sees the number and the time.</li>
<li><strong>On your computer,</strong> the app keeps no texts. Your sign-in stays in your system’s credential store (Windows Credential Manager, or your Linux keyring), your password is never stored, and the app has no ads, analytics or tracking.</li>
</ul>
<h3 id="coming-end-to-end-private-texts">Coming: end-to-end private texts</h3>
<p>We’re building end-to-end private texts for the desktop app, on every plan. Your messages will be readable only on your phone and on your computers: smstunnel.io will keep only encrypted copies it can’t read. We’ll write about it here when it’s ready.</p>
<p>The encryption is about SMSTunnel’s part of the journey. Before a text reaches your phone, and after your phone sends one, it travels over the mobile network like any other SMS.</p>
<p>Texts your own software sends already have their own option: end-to-end encryption, on every plan, where your software encrypts each text for your phone and SMSTunnel can’t read it. See <a href="https://smstunnel.io/blog/end-to-end-encrypted-sms/">end-to-end encrypted SMS</a>.</p>
<h2 id="the-dashboard-and-when-to-use-which">The dashboard, and when to use which</h2>
<p>The dashboard works in any browser and needs nothing installed, on a Mac too, until the macOS version comes. The desktop app adds notifications, the tray and starting with your computer. Both show the same account and the same texts, so use whichever is at hand.</p>
<h2 id="common-questions">Common questions</h2>
<h3 id="is-the-desktop-app-free">Is the desktop app free?</h3>
<p>Yes, on Windows and on Linux. Your SMSTunnel plan sets how many texts you can send each day, and your mobile operator charges each text like any SMS from your phone.</p>
<h3 id="which-systems-does-it-run-on">Which systems does it run on?</h3>
<p>Windows 10 and 11 (64-bit), from the Microsoft Store or with the installer from the download page, and 64-bit Linux: an AppImage, a .deb for Ubuntu, Debian and Mint, and an .rpm for Fedora and openSUSE. A macOS version comes later.</p>
<h3 id="can-smstunnel-read-the-texts-i-receive">Can SMSTunnel read the texts I receive?</h3>
<p>Synced texts are stored in your account in readable form, so the dashboard and the app can show them; they aren’t end-to-end encrypted yet. Our staff don’t look at your messages, except when that’s needed to answer a support request you send us or to investigate abuse. End-to-end private texts for the desktop app, which smstunnel.io won’t be able to read, are coming, on every plan.</p>
<h3 id="do-i-still-need-my-phone">Do I still need my phone?</h3>
<p>Yes. The desktop app reads and sends texts through your own phone, and every text goes out from your own number. The phone must be online with the SMSTunnel app running; if it’s offline, a text waits and goes out when the phone is back, for up to 3 days.</p>]]></content:encoded>
    </item>
    <item>
      <title>Multi-tenant SMS: every client of your app texts from their own number</title>
      <link>https://smstunnel.io/blog/multi-tenant-sms-each-client-own-number/</link>
      <guid isPermaLink="true">https://smstunnel.io/blog/multi-tenant-sms-each-client-own-number/</guid>
      <pubDate>Tue, 03 Nov 2026 09:00:00 GMT</pubDate>
      <dc:creator>NARBOWEB SRL</dc:creator>
      <category>Enterprise</category>
      <category>SDK</category>
      <description>Your app serves many businesses. On Enterprise, each client links their own Android phone by QR code, and your app texts their customers from their number.</description>
      <content:encoded><![CDATA[<p>Your app serves many businesses. An invoicing app, a booking system, a platform for schools or clinics: each of your clients has its own customers, and when your app texts those customers, the text should come from your client. Not from a number you rented that nobody recognizes, and not from your own company.</p>
<p>Picture an invoicing app used by a few hundred small companies. When one of them issues an invoice, its customer gets a text: “Acme Dental: invoice no. 2087 has been issued. Total: $240.” With SMSTunnel’s Enterprise plan, that text goes out from Acme Dental’s own Android phone, from Acme Dental’s own number. The customer knows who wrote, and a reply or a call back reaches Acme Dental, not you.</p>
<p>This guide walks through the whole flow: your app links each client’s own phone with a QR code, gets that client’s key back on its own, and hands their texts to their phone. You’ll see the code with the Node.js SDK, what your dashboard shows, a real deployment, and an honest comparison with the usual workarounds.</p>
<h2 id="why-each-clients-own-number-matters">Why each client’s own number matters</h2>
<ul>
<li><strong>People know the number.</strong> A text from the business they deal with every day reads as a message from that business, because it is one.</li>
<li><strong>Replies reach the right people.</strong> When a customer answers, the reply lands on your client’s phone. You don’t end up running a support inbox for your clients’ customers.</li>
<li><strong>Each client uses their own SIM and plan.</strong> SMSTunnel charges no per-message fees, so the texts a client’s phone sends go on that client’s mobile plan, at their carrier’s rates.</li>
<li><strong>No numbers to rent or register.</strong> Your clients already have a phone and a number. Your app only needs to link them.</li>
</ul>
<h2 id="how-the-enterprise-flow-works">How the Enterprise flow works</h2>
<p>The flow has five steps, and only one of them involves your client:</p>
<ol>
<li><strong>You create an Enterprise app</strong> in your SMSTunnel dashboard and get its key. That key carries the <code>enterprise:saas</code> permission, which links clients; other keys get a 403.</li>
<li><strong>Your backend starts linking a client</strong> with <code>activateEnterpriseClient()</code>: the client’s name, your own id for them (<code>externalId</code>) and a callback URL. SMSTunnel makes the client’s account (or reuses it) and returns a QR code and the client’s own API key.</li>
<li><strong>Your client scans the QR code.</strong> They install the SMSTunnel app from Google Play on their own Android phone, open <strong>Connections</strong>, tap <strong>Link with QR code</strong> and scan the code your app shows them.</li>
<li><strong>SMSTunnel calls your callback URL</strong> with a <code>pairing_completed</code> event, the token from the QR code and the client’s key.</li>
<li><strong>Your app sends that client’s texts with their key.</strong> Each text goes to their phone, which sends it from their number. <code>getEnterpriseClient(externalId)</code> tells you at any time whether the phone is linked and online.</li>
</ol>
<p>Your client never copies an API key and never opens SMSTunnel’s dashboard: they install the app and scan one code.</p>
<h2 id="step-by-step-with-the-nodejs-sdk">Step by step with the Node.js SDK</h2>
<p>The Enterprise calls are in the official SDK, <a href="https://www.npmjs.com/package/@narcisbodea/smstunnel-sdk"><code>@narcisbodea/smstunnel-sdk</code></a>, both as a standalone Node.js client and as a NestJS module.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> install</span><span style="color:#9ECBFF"> @narcisbodea/smstunnel-sdk</span></span></code></pre>
<h3 id="1-create-the-enterprise-app">1. Create the Enterprise app</h3>
<p>On the Enterprise plan your dashboard has a <strong>Company</strong> section. Under <strong>Company → Apps</strong>, choose <strong>Add an app</strong>: one app for each product of yours that serves clients. Copy its key and keep it in an environment variable on your server, never in code that runs in a browser or a mobile app.</p>
<h3 id="2-start-linking-a-client">2. Start linking a client</h3>
<p>When a client turns on texting in your app, your backend asks SMSTunnel for their QR code:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { SmsTunnelNodeClient } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '@narcisbodea/smstunnel-sdk/node'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// The key of your Enterprise app (Company → Apps).</span></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> company</span><span style="color:#F97583"> =</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> SmsTunnelNodeClient</span><span style="color:#E1E4E8">({ apiKey: process.env.</span><span style="color:#79B8FF">SMSTUNNEL_ENTERPRISE_API_KEY</span><span style="color:#F97583">!</span><span style="color:#E1E4E8"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> activation</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#E1E4E8"> company.</span><span style="color:#B392F0">activateEnterpriseClient</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#E1E4E8">  clientName: </span><span style="color:#9ECBFF">'Acme Dental'</span><span style="color:#E1E4E8">,                                  </span><span style="color:#6A737D">// shown in the app and in your dashboard</span></span>
<span class="line"><span style="color:#E1E4E8">  externalId: </span><span style="color:#9ECBFF">'tenant-42'</span><span style="color:#E1E4E8">,                                    </span><span style="color:#6A737D">// your own id for this client</span></span>
<span class="line"><span style="color:#E1E4E8">  callbackUrl: </span><span style="color:#9ECBFF">'https://invoices.example.com/smstunnel/callback'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Keep these with the client's record in your own database:</span></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> clientKey</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> activation.apiKey.key;      </span><span style="color:#6A737D">// works for sending once their phone has linked</span></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> pairingToken</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> activation.pairingToken; </span><span style="color:#6A737D">// the token the callback will carry</span></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> qrData</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> activation.activation.qrData;  </span><span style="color:#6A737D">// show this as a QR code</span></span></code></pre>
<p>The client’s key is a secret like any other API key: store it on your server only.</p>
<h3 id="3-show-the-qr-code">3. Show the QR code</h3>
<p>Render <code>qrData</code> with any QR code library, on the page where your client turns on texting. They scan it with the SMSTunnel app, and the app shows the client name you sent, so they can see what they are linking.</p>
<h3 id="4-handle-the-callback">4. Handle the callback</h3>
<p>When the phone links, SMSTunnel posts <code>pairing_completed</code> to your <code>callbackUrl</code>, which must be an https address on a public host. Accept it only when its token is one you got from step 2:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#E1E4E8">app.</span><span style="color:#B392F0">post</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'/smstunnel/callback'</span><span style="color:#E1E4E8">, express.</span><span style="color:#B392F0">json</span><span style="color:#E1E4E8">(), </span><span style="color:#F97583">async</span><span style="color:#E1E4E8"> (</span><span style="color:#FFAB70">req</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">res</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=></span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#F97583">  const</span><span style="color:#E1E4E8"> { </span><span style="color:#79B8FF">event</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">token</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">apiKey</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> req.body;</span></span>
<span class="line"><span style="color:#F97583">  const</span><span style="color:#79B8FF"> tenant</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#E1E4E8"> db.tenants.</span><span style="color:#B392F0">findOne</span><span style="color:#E1E4E8">({ smstunnelPairingToken: token }); </span><span style="color:#6A737D">// your own database</span></span>
<span class="line"><span style="color:#F97583">  if</span><span style="color:#E1E4E8"> (event </span><span style="color:#F97583">!==</span><span style="color:#9ECBFF"> 'pairing_completed'</span><span style="color:#F97583"> ||</span><span style="color:#F97583"> !</span><span style="color:#E1E4E8">tenant) </span><span style="color:#F97583">return</span><span style="color:#E1E4E8"> res.</span><span style="color:#B392F0">sendStatus</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">404</span><span style="color:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">  await</span><span style="color:#E1E4E8"> db.tenants.</span><span style="color:#B392F0">update</span><span style="color:#E1E4E8">(tenant.id, { smstunnelKey: apiKey, smsReady: </span><span style="color:#79B8FF">true</span><span style="color:#E1E4E8"> });</span></span>
<span class="line"><span style="color:#E1E4E8">  res.</span><span style="color:#B392F0">sendStatus</span><span style="color:#E1E4E8">(</span><span style="color:#79B8FF">200</span><span style="color:#E1E4E8">);</span></span>
<span class="line"><span style="color:#E1E4E8">});</span></span></code></pre>
<h3 id="5-send-texts-from-the-clients-phone">5. Send texts from the client’s phone</h3>
<p>Each client’s texts go through a client object made with that client’s key. One text goes to one number:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { SmsTunnelNodeClient, SmsTunnelApiError } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '@narcisbodea/smstunnel-sdk/node'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">async</span><span style="color:#F97583"> function</span><span style="color:#B392F0"> textInvoiceIssued</span><span style="color:#E1E4E8">(</span><span style="color:#FFAB70">tenant</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">invoice</span><span style="color:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#F97583">  const</span><span style="color:#79B8FF"> sms</span><span style="color:#F97583"> =</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> SmsTunnelNodeClient</span><span style="color:#E1E4E8">({ apiKey: tenant.smstunnelKey });</span></span>
<span class="line"><span style="color:#F97583">  try</span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#F97583">    await</span><span style="color:#E1E4E8"> sms.</span><span style="color:#B392F0">sendSms</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#E1E4E8">      invoice.customerPhone, </span><span style="color:#6A737D">// international format, like +14155550123</span></span>
<span class="line"><span style="color:#9ECBFF">      `${</span><span style="color:#E1E4E8">tenant</span><span style="color:#9ECBFF">.</span><span style="color:#E1E4E8">name</span><span style="color:#9ECBFF">}: invoice no. ${</span><span style="color:#E1E4E8">invoice</span><span style="color:#9ECBFF">.</span><span style="color:#E1E4E8">number</span><span style="color:#9ECBFF">} has been issued. Total: ${</span><span style="color:#E1E4E8">invoice</span><span style="color:#9ECBFF">.</span><span style="color:#E1E4E8">total</span><span style="color:#9ECBFF">}.`</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">    );</span></span>
<span class="line"><span style="color:#E1E4E8">  } </span><span style="color:#F97583">catch</span><span style="color:#E1E4E8"> (error) {</span></span>
<span class="line"><span style="color:#F97583">    if</span><span style="color:#E1E4E8"> (error </span><span style="color:#F97583">instanceof</span><span style="color:#B392F0"> SmsTunnelApiError</span><span style="color:#E1E4E8">) console.</span><span style="color:#B392F0">error</span><span style="color:#E1E4E8">(error.status, error.message);</span></span>
<span class="line"><span style="color:#F97583">    else</span><span style="color:#F97583"> throw</span><span style="color:#E1E4E8"> error;</span></span>
<span class="line"><span style="color:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<h3 id="6-check-whether-a-clients-phone-is-linked-and-online">6. Check whether a client’s phone is linked and online</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#F97583">const</span><span style="color:#E1E4E8"> { </span><span style="color:#79B8FF">activation</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#F97583"> await</span><span style="color:#E1E4E8"> company.</span><span style="color:#B392F0">getEnterpriseClient</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'tenant-42'</span><span style="color:#E1E4E8">);</span></span>
<span class="line"><span style="color:#6A737D">// activation?.status: 'pending' | 'connected' | 'expired' | 'cancelled'</span></span>
<span class="line"><span style="color:#6A737D">// activation?.isOnline: whether their phone is online right now</span></span></code></pre>
<p>Show that status next to the client in your own app, so they know whether texting works before they rely on it.</p>
<h3 id="with-nestjs">With NestJS</h3>
<p>If your backend is NestJS, the SDK’s module has an Enterprise mode: pass your Enterprise key and the current organization’s id, and <code>SmsTunnelService</code> does the linking and the sending for that organization.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#E1E4E8">SmsTunnelModule.</span><span style="color:#B392F0">forRootAsync</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#B392F0">  useFactory</span><span style="color:#E1E4E8">: (</span><span style="color:#FFAB70">settingsModel</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">tenantService</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=></span><span style="color:#E1E4E8"> ({</span></span>
<span class="line"><span style="color:#E1E4E8">    storage: </span><span style="color:#F97583">new</span><span style="color:#B392F0"> MongoSmsTunnelStorage</span><span style="color:#E1E4E8">(settingsModel),</span></span>
<span class="line"><span style="color:#E1E4E8">    callbackBaseUrl: process.env.</span><span style="color:#79B8FF">CALLBACK_URL</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">    displayName: </span><span style="color:#9ECBFF">'My App'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">    enterpriseApiKey: process.env.</span><span style="color:#79B8FF">SMSTUNNEL_ENTERPRISE_API_KEY</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">    externalId: tenantService.</span><span style="color:#B392F0">getCurrentTenantId</span><span style="color:#E1E4E8">(),</span></span>
<span class="line"><span style="color:#E1E4E8">  }),</span></span>
<span class="line"><span style="color:#E1E4E8">  inject: [</span><span style="color:#B392F0">getModelToken</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'Settings'</span><span style="color:#E1E4E8">), TenantService],</span></span>
<span class="line"><span style="color:#E1E4E8">})</span></span></code></pre>
<p><code>await smsTunnelService.getEnterpriseClientStatus()</code> then tells you whether that organization’s phone has linked and is online. The other storage adapters (Prisma, a JSON file) are in the <a href="https://github.com/narcisbodea/smstunnel-sdk">SDK’s README</a>.</p>
<h2 id="what-your-dashboard-shows">What your dashboard shows</h2>
<p>The <strong>Company</strong> section has five tabs: <strong>Overview</strong>, <strong>Clients</strong>, <strong>Phones</strong>, <strong>Apps</strong> and <strong>Invitations</strong>. Clients and phones are many-to-many: a client can link more than one phone, and one phone can serve several clients. That is how a client without a phone of their own can still send: under <strong>Phones</strong> you can share one of your own phones with your clients. Each client and each phone has its own page, with the texts it sent over the last 30 days and the texts still waiting for a phone.</p>
<p>You don’t need code for every client either. Under <strong>Invitations</strong> you create a QR code for a client, send it to them or show it, and they scan it in the SMSTunnel app to join your company. An app with its own key is only needed when your software links clients by itself.</p>
<h2 id="a-real-deployment-one-office-in-every-county">A real deployment: one office in every county</h2>
<p>One of the apps built on this flow serves a public education network with an office in each county. The app keeps a single Enterprise key in its settings. Each county office is a client with its own <code>externalId</code>; someone at the office scans the QR code once with the office’s phone, and from then on the app sends that office’s texts from that office’s own number. People get texts from the local office they deal with, and when they call back, they reach that office.</p>
<h2 id="things-to-know-before-you-build">Things to know before you build</h2>
<ul>
<li><strong>One recipient per text.</strong> Each call sends one text, up to 1600 characters, to one number. For several people, make one call per person.</li>
<li><strong>Pace your sending.</strong> The API accepts 30 send requests a minute per API key on each send endpoint (all the keys of one account together get three times that), so if your server sends for many clients at once, queue the texts and retry after a 429 when the <code>Retry-After</code> time has passed.</li>
<li><strong>Phones go offline.</strong> If a client’s phone is off, their texts wait and go out when it is back, for up to 3 days; after that they are marked failed.</li>
<li><strong>Linking again is safe.</strong> Calling <code>activateEnterpriseClient()</code> again for the same <code>externalId</code> replaces the earlier activation and its key, while a phone that already linked stays linked. Two overlapping calls for one client can get a 409 with <code>code: 'CONCURRENT_ACTIVATION'</code> or <code>'ALREADY_CONNECTED'</code>.</li>
<li><strong>With the NestJS module, a new QR code starts a new link.</strong> Since SDK 2.2.0, <code>createPairingToken()</code> clears the organization’s stored key until its phone scans the new code, so its texts stop in between. Offer a QR code only while no phone is linked (<code>getEnterpriseClientStatus()</code> tells you).</li>
<li><strong>Your clients stay in charge of their phones.</strong> A phone’s owner can unlink it in the app or in the dashboard.</li>
<li><strong>Privacy, if you need it.</strong> Your app can <a href="https://smstunnel.io/blog/end-to-end-encrypted-sms/">encrypt each text end to end</a> for the phone that sends it, and the Enterprise plan includes Maximum privacy, where the number is sealed too.</li>
</ul>
<h2 id="how-this-compares-to-other-ways-of-doing-it">How this compares to other ways of doing it</h2>
<p>Other tools that send texts from an Android phone, such as httpSMS and textbee, are built around one account and its own phones. From our comparison pages, checked in November 2026:</p>
<ul>
<li><strong>httpSMS:</strong> phone keys put several phones in one account, and each key is made by hand in its dashboard. See <a href="https://smstunnel.io/compare/httpsms/">SMSTunnel vs httpSMS</a>.</li>
<li><strong>textbee:</strong> phones belong to one account, 1, 5 or 15 depending on the plan, with no accounts for your clients. See <a href="https://smstunnel.io/compare/textbee/">SMSTunnel vs textbee</a>.</li>
</ul>
<p>With either, the usual workaround for a multi-tenant app is to ask each client to open their own account, link their phone there, create an API key and paste it into your app. It works, but every client has to understand API keys, your support team has to explain them, and you never see your clients’ phones in one place.</p>
<p>SMS API providers such as <a href="https://smstunnel.io/compare/twilio/">Twilio</a> solve a different problem: they send from numbers you rent from them. That suits high volumes and many countries, but the texts don’t come from your clients’ own numbers.</p>
<h2 id="what-it-costs">What it costs</h2>
<p>The Enterprise plan is $99 a month, or $990 a year, before tax. It has no daily cap (texts are rate-limited to protect the SIMs), sign-in codes with no daily cap, separate API keys for each of your apps, Maximum privacy and dedicated support. SMSTunnel adds no per-message fees: each text uses the mobile plan of the phone that sends it. The <a href="https://smstunnel.io/pricing/">pricing page</a> has every plan, and <a href="https://smstunnel.io/blog/sms-cost-own-sim-vs-sms-api/">this comparison of costs</a> explains what an own SIM costs next to an SMS API.</p>
<p>Want to try the phone side first? Any plan, including Free, lets you link your own phone and send from your own software. When you are ready to link your clients’ phones, the Enterprise plan adds the Company section and the calls above, and if you’d like help connecting your own systems, <a href="https://smstunnel.io/contact/">write to us</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>End-to-end encrypted texts from your own software: what is and isn't encrypted</title>
      <link>https://smstunnel.io/blog/end-to-end-encrypted-sms/</link>
      <guid isPermaLink="true">https://smstunnel.io/blog/end-to-end-encrypted-sms/</guid>
      <pubDate>Tue, 03 Nov 2026 08:50:00 GMT</pubDate>
      <dc:creator>NARBOWEB SRL</dc:creator>
      <category>Encryption</category>
      <category>Privacy</category>
      <description>Your software encrypts each text with your phone's public key, so SMSTunnel can't read it. What is encrypted today, what isn't, and how Maximum privacy works.</description>
      <content:encoded><![CDATA[<p>When your website, shop or script sends a text through SMSTunnel, the text travels from your server to your SMSTunnel account and from there to your own Android phone, which sends it from your number. End-to-end encryption closes that path to everyone in the middle, including us: your software locks the text with your phone’s public key before it leaves your server, and only your phone can unlock it.</p>
<p>It’s optional, and it’s included on every plan, even Free. This article explains how it works, which integrations support it, how to turn it on, and, just as important, what it does not cover. Encryption you can’t describe exactly is worth little, so the limits come first-class here, not in a footnote.</p>
<h2 id="how-it-works">How it works</h2>
<p>There are three steps, and only the first and the last can read the text:</p>
<ol>
<li><strong>Your software encrypts.</strong> It locks the text with your phone’s public key, on your own server, before anything leaves it. Each phone has its own RSA-2048 key pair; the private key never leaves the phone, and SMSTunnel stores only the public key.</li>
<li><strong>SMSTunnel carries.</strong> Our servers pass the locked text on, and erase it once it’s sent. We can’t read the message.</li>
<li><strong>Your phone sends.</strong> Only your phone holds the private key. It unlocks the text and sends it as a regular SMS from your number.</li>
</ol>
<p>What SMSTunnel still sees with encryption on: your account, your phone and the time of each text, plus the number the text goes to, unless you use Maximum privacy (below).</p>
<h2 id="what-is-encrypted-and-what-isnt">What is encrypted, and what isn’t</h2>





































<table><thead><tr><th>Text</th><th>End-to-end encrypted?</th></tr></thead><tbody><tr><td>Texts your software encrypts (the API, the Node.js SDK, the WordPress plugin with encryption on)</td><td>Yes, the words. The number stays visible in your account so your Messages page can show it</td></tr><tr><td>The same texts with Maximum privacy (Professional and Enterprise)</td><td>Yes, the words and the number</td></tr><tr><td>Texts you type in the dashboard</td><td>No. Protected in transit and kept readable in your account, so you can see your history</td></tr><tr><td>Texts your software sends without encryption</td><td>No. Kept readable in your account history</td></tr><tr><td>Sign-in codes from the WordPress plugin</td><td>No. The plugin doesn’t encrypt them</td></tr><tr><td>Texts your phone receives, if you sync them</td><td>No. Protected in transit and stored readable, so the dashboard can show them</td></tr><tr><td>The SMS itself, between your phone and the person you text</td><td>No. After your phone sends it, it travels over the mobile network like any other SMS</td></tr></tbody></table>
<p>All traffic to and from SMSTunnel uses encrypted connections (TLS 1.2 or higher), whether or not a text is end-to-end encrypted. The table is about who can read the words once they arrive.</p>
<p>On received texts: end-to-end encryption for them, readable only on your phone and in a desktop app that’s coming, is being built.</p>
<h2 id="turn-it-on-in-wordpress">Turn it on in WordPress</h2>
<p>The <a href="https://smstunnel.io/integrations/smstunnel/">SMSTunnel plugin for WordPress</a> encrypts its texts for your phone before they leave WordPress. It needs your phone’s public key once, which it gets with a QR code:</p>
<ol>
<li>In WordPress, go to <strong>SMSTunnel → Encryption Settings</strong>.</li>
<li>Start the encryption pairing to show a QR code.</li>
<li>Scan the QR code with the SMSTunnel app on your phone.</li>
<li>Done: texts from your site are now end-to-end encrypted.</li>
</ol>
<p>During the pairing the phone shares only its public key. Its private key never leaves the phone, so SMSTunnel stores only encrypted text that it cannot read. Keep in mind the one exception from the table: the plugin’s sign-in codes for WordPress logins are not encrypted.</p>
<h2 id="encrypt-from-your-own-code">Encrypt from your own code</h2>
<h3 id="with-the-nodejs-sdk">With the Node.js SDK</h3>
<p>The official SDK does the whole job in one call: it fetches your phone’s public key, encrypts the number and the words, and sends the result.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { SmsTunnelNodeClient } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '@narcisbodea/smstunnel-sdk/node'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> client</span><span style="color:#F97583"> =</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> SmsTunnelNodeClient</span><span style="color:#E1E4E8">({ apiKey: process.env.</span><span style="color:#79B8FF">SMSTUNNEL_API_KEY</span><span style="color:#F97583">!</span><span style="color:#E1E4E8"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D">// Encrypts { to, message } with the phone's public key, then sends it.</span></span>
<span class="line"><span style="color:#F97583">await</span><span style="color:#E1E4E8"> client.</span><span style="color:#B392F0">encryptAndSendSms</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'+14155550123'</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">'Your order has shipped.'</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">'device-id'</span><span style="color:#E1E4E8">);</span></span></code></pre>
<p>The <code>device-id</code> is the phone that will send the text: <code>client.getDevices()</code> lists your phones with their ids. In the SDK’s NestJS module, <code>smsTunnelService.encryptAndSendSms(to, message)</code> exchanges keys with the phone the first time and encrypts from then on.</p>
<p>Encryption has to run on your server. Browsers can’t do it with the SDK, and your API key must never reach a browser anyway.</p>
<h3 id="with-the-rest-api-in-any-language">With the REST API, in any language</h3>
<p>Without the SDK, the steps are the same:</p>
<ol>
<li>Get the phone’s public key with <code>GET /api/v1/devices/{deviceId}/public-key</code>.</li>
<li>Encrypt the JSON <code>{"to": "+14155550123", "message": "…"}</code> with it, on your server.</li>
<li>Send the result to <code>POST /api/v1/sms/send</code> as <code>encryptedPayload</code> (base64), with <code>"encrypted": true</code>, the <code>deviceId</code> and, as well, <code>to</code>, so your Messages page can show the number.</li>
</ol>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="json"><code><span class="line"><span style="color:#E1E4E8">{</span></span>
<span class="line"><span style="color:#79B8FF">  "to"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"+14155550123"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#79B8FF">  "deviceId"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"671fe0d9c3b2a1f0e9d8c7b6"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#79B8FF">  "encrypted"</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">true</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#79B8FF">  "encryptedPayload"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"BASE64_OF_THE_ENCRYPTED_JSON"</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>You don’t have to work out the encryption format yourself: when you add an app in your dashboard and choose to encrypt end to end, it gives you ready code for PHP, Node.js, Python and C#. The <a href="https://smstunnel.io/api-reference/#e2e-public-key">API reference</a> has the details.</p>
<p>One practical difference: an encrypted text counts toward your plan’s daily limit and is refused past it, because it can’t carry the short “free by smstunnel.io” line that the Free plan adds to the extra texts of the day.</p>
<h3 id="notice-when-the-phones-key-changes">Notice when the phone’s key changes</h3>
<p>If the SMSTunnel app is reinstalled, the phone makes a new key pair. <code>GET /api/v1/devices/{deviceId}/e2e-status</code> returns whether encryption is on, whether a public key is stored, its fingerprint (the first 16 hex characters of a SHA-256 hash) and when it was created. Store the fingerprint after the first pairing and compare it before you send:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> result</span><span style="color:#F97583"> =</span><span style="color:#F97583"> await</span><span style="color:#E1E4E8"> client.</span><span style="color:#B392F0">verifyDeviceKey</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'device-id'</span><span style="color:#E1E4E8">, storedFingerprint);</span></span>
<span class="line"><span style="color:#F97583">if</span><span style="color:#E1E4E8"> (result.needsRePairing) {</span></span>
<span class="line"><span style="color:#6A737D">  // The app was reinstalled and the key changed: pair again before sending.</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<h2 id="maximum-privacy-hide-the-number-too">Maximum privacy: hide the number too</h2>
<p>End-to-end encryption hides the words. On the Professional and Enterprise plans, Maximum privacy hides the number as well, so we can’t see who a text is for.</p>
<p>It works with a private certificate that you make, protected by a password only you know. You put the certificate in your software and on your phone, the key never passes through our servers, and from then on your phone refuses any text that isn’t sealed with it, including texts written in the dashboard.</p>
<h3 id="1-make-the-certificate">1. Make the certificate</h3>
<p>In your dashboard: <strong>Apps → Add an app → Encrypt end to end → Maximum privacy</strong>, which also gives you ready code for PHP, Node.js, Python and C#. Or make it on your own computer, where nothing leaves the machine:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">npx</span><span style="color:#79B8FF"> -p</span><span style="color:#9ECBFF"> @narcisbodea/smstunnel-sdk</span><span style="color:#9ECBFF"> smstunnel-make-certificate</span></span></code></pre>
<p>It asks for a password of at least 12 characters and saves <code>smstunnel-certificate.json</code>.</p>
<h3 id="2-put-it-on-your-phone">2. Put it on your phone</h3>
<p>In the SMSTunnel app (version 1.0.9 or newer), open <strong>Settings → Private certificate</strong> and paste the contents of the file. If you’d rather scan a QR code, the dashboard shows one under <strong>Apps → Maximum privacy → “I already have one”</strong>.</p>
<h3 id="3-send-sealed-texts">3. Send sealed texts</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { readFileSync } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> 'node:fs'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { SmsTunnelNodeClient, openCertificate } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '@narcisbodea/smstunnel-sdk/node'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> client</span><span style="color:#F97583"> =</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> SmsTunnelNodeClient</span><span style="color:#E1E4E8">({ apiKey: process.env.</span><span style="color:#79B8FF">SMSTUNNEL_API_KEY</span><span style="color:#F97583">!</span><span style="color:#E1E4E8"> });</span></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> certificate</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> openCertificate</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#B392F0">  readFileSync</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'smstunnel-certificate.json'</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">'utf8'</span><span style="color:#E1E4E8">),</span></span>
<span class="line"><span style="color:#E1E4E8">  process.env.</span><span style="color:#79B8FF">SMSTUNNEL_CERT_PASSWORD</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">await</span><span style="color:#E1E4E8"> client.</span><span style="color:#B392F0">sendSealedSms</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'+14155550123'</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">'Your order has shipped.'</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">'device-id'</span><span style="color:#E1E4E8">, certificate);</span></span></code></pre>
<p>Over the REST API, a sealed text is sent as <code>encryptedPayload</code> with <code>"encrypted": true</code> and the <code>deviceId</code>, and without <code>to</code>.</p>
<p>What to know before you switch it on:</p>
<ul>
<li>We still see your account, your phone and the time of each text.</li>
<li>If you sync received texts to your account, a reply shows the number it came from.</li>
<li>Your software needs the certificate and its password to send. Keep the password out of your code, for example in an environment variable.</li>
<li>We can’t recover a lost certificate or password. If you lose either, make a new certificate and put it on your phone and in your software again.</li>
<li>On the other plans, a text without <code>to</code>, or one sealed with a certificate, is refused with status 403.</li>
</ul>
<h2 id="how-this-compares">How this compares</h2>
<p>Among the tools that send texts from your own Android phone, <a href="https://smstunnel.io/compare/httpsms/">httpSMS</a> also offers optional end-to-end encryption: you set one key in its app and use the same key in your code, for sent and received texts. For <a href="https://smstunnel.io/compare/textbee/">textbee</a>, we found no end-to-end encryption in its documentation, which says its API traffic uses HTTPS and that it stores your messages on its servers. SMSTunnel uses a key pair per phone, so the key that unlocks your texts never leaves the phone, and adds Maximum privacy for the number on Professional and Enterprise.</p>
<h2 id="common-questions">Common questions</h2>
<h3 id="can-smstunnel-read-my-encrypted-texts">Can SMSTunnel read my encrypted texts?</h3>
<p>No. We only ever have the phone’s public key, which can lock a text but not unlock it, and we erase the locked text once it’s sent.</p>
<h3 id="is-the-sms-encrypted-on-its-way-to-the-person-i-text">Is the SMS encrypted on its way to the person I text?</h3>
<p>No. End-to-end here means from your software to your phone. Once your phone sends the text, it is a regular SMS on the mobile network.</p>
<h3 id="does-encryption-cost-extra">Does encryption cost extra?</h3>
<p>No. It’s on every plan, including Free. Maximum privacy comes with Professional ($39 a month) and Enterprise ($99 a month); see the <a href="https://smstunnel.io/pricing/">pricing page</a>.</p>
<h3 id="can-apps-with-many-clients-use-it">Can apps with many clients use it?</h3>
<p>Yes. On Enterprise, <a href="https://smstunnel.io/blog/multi-tenant-sms-each-client-own-number/">each client links their own phone</a>, and your app can encrypt each text for the phone that will send it.</p>]]></content:encoded>
    </item>
    <item>
      <title>What it costs to send texts from your own SIM vs. an SMS API</title>
      <link>https://smstunnel.io/blog/sms-cost-own-sim-vs-sms-api/</link>
      <guid isPermaLink="true">https://smstunnel.io/blog/sms-cost-own-sim-vs-sms-api/</guid>
      <pubDate>Tue, 03 Nov 2026 08:40:00 GMT</pubDate>
      <dc:creator>NARBOWEB SRL</dc:creator>
      <category>Pricing</category>
      <category>SMS API</category>
      <description>What sending texts really costs: per-segment prices, rented numbers and US registration with an SMS API, or a flat plan plus your own phone's SIM.</description>
      <content:encoded><![CDATA[<p>Every text your website, shop or app sends costs something. With an SMS API provider such as Twilio, Vonage or Plivo, your software sends from numbers you rent, and you pay for each text. With SMSTunnel, your own Android phone sends the texts from its SIM and your own number: you pay a flat plan for the service, and the texts go over your own mobile plan.</p>
<p>This guide puts numbers on both. The providers’ prices below are the US list prices our <a href="https://smstunnel.io/compare/">comparison pages</a> recorded in November 2026. They differ by country and change over time, so check each provider’s own pricing for the countries you text. SMSTunnel’s plans are the ones on the <a href="https://smstunnel.io/pricing/">pricing page</a>.</p>
<p>One caution before the numbers: the two models don’t do the same job. An SMS API delivers every text over the carrier networks itself, and its price per text pays for that. With SMSTunnel, your phone does the delivery, so each text is paid for by your mobile plan, not by SMSTunnel. The right choice depends on what you send, how much, and to whom.</p>
<h2 id="what-an-sms-api-charges-for">What an SMS API charges for</h2>
<p>A provider’s bill has several lines. These are the US prices from our comparison pages:</p>



































<table><thead><tr><th></th><th>Twilio</th><th>Vonage</th><th>Plivo</th></tr></thead><tbody><tr><td>Each text sent</td><td>$0.0083 per segment</td><td>$0.0086 per text</td><td>$0.0077 per segment</td></tr><tr><td>Each text received</td><td>$0.0083 per segment</td><td>$0.00691 per text</td><td>$0.0077 per segment</td></tr><tr><td>A local number</td><td>$1.15 a month</td><td>$1.09 to set up, first month included</td><td>$0.50 a month</td></tr><tr><td>Minimum spend</td><td>None</td><td>None</td><td>SMS only on its Enterprise plan, from $1,000 a month</td></tr></tbody></table>
<p>On top of that:</p>
<ul>
<li><strong>Carrier fees.</strong> US carriers add their own fee for each text sent, which the provider passes on: for example $0.0035 (AT&#x26;T), $0.0045 (T-Mobile) or $0.005 (Verizon). Some carriers charge for received texts too.</li>
<li><strong>Registration.</strong> To text US customers as a business from a local number, you register your brand and each use case for A2P 10DLC. At Twilio that’s a one-time brand fee of $4.50 (or $46 for a standard brand), $15 to review each use case, and $1.50 to $10 a month for each one. Vonage and Plivo charge similar fees, and Vonage adds $41.50 for the vetting most use cases need.</li>
<li><strong>Failed texts.</strong> Twilio charges $0.001 for each text that ends up failed.</li>
<li><strong>Replies.</strong> Every reply your customers send is a received text, and it’s billed like one.</li>
</ul>
<h3 id="long-texts-and-special-characters">Long texts and special characters</h3>
<p>Providers bill per segment, and a segment holds up to 160 characters. A longer text goes out as several segments joined together, and each one counts. Characters outside the basic GSM alphabet, such as accented letters like ă or ș and most emoji, usually cut a segment to 70 characters, so the same message can cost two or three times as much. Your mobile plan usually counts the parts of a long text the same way, so short texts pay off with either model.</p>
<h2 id="what-you-pay-when-your-own-phone-sends">What you pay when your own phone sends</h2>
<p>SMSTunnel charges a flat price, monthly or yearly, with nothing per text, nothing per number and no registration:</p>



































<table><thead><tr><th>Plan</th><th>Price</th><th>What’s included</th></tr></thead><tbody><tr><td>Free</td><td>$0</td><td>25 texts a day and 500 a month, sent as you wrote them, plus up to 25 more a day from your own software ending with a short “free by smstunnel.io” line; 50 sign-in codes a day</td></tr><tr><td>Starter</td><td>$9 a month</td><td>300 texts a day and 5,000 a month; sign-in codes with no daily cap</td></tr><tr><td>Standard</td><td>$19 a month</td><td>2,000 texts a day and 30,000 a month</td></tr><tr><td>Professional</td><td>$39 a month</td><td>No daily cap (rate-limited to protect your SIM); Maximum privacy</td></tr><tr><td>Enterprise</td><td>$99 a month</td><td>No daily cap; your organization’s own phones; separate API keys for each of your apps</td></tr></tbody></table>
<p>Paying yearly costs ten months: $90, $190, $390 or $990 a year. Prices are in US dollars, before tax; where tax applies, it’s added at checkout and shown on your invoice. Plans are month to month or yearly, with no contract. Only the texts you send count toward a plan: the texts your phone receives don’t.</p>
<p>Then there’s your mobile plan. The texts leave from your SIM, so your carrier’s normal SMS charges or allowance apply, including for texts to other countries. Many plans include unlimited texts, but a personal plan may not allow business use: check your plan before your shop starts texting customers from it.</p>
<h2 id="two-worked-examples">Two worked examples</h2>
<p>The examples below use US prices and single-segment texts. Your own numbers will differ, but the shape of the bill won’t.</p>
<h3 id="a-shop-sending-1000-order-texts-a-month">A shop sending 1,000 order texts a month</h3>
<p>With Twilio:</p>
<ul>
<li>1,000 texts × $0.0083 = $8.30</li>
<li>carrier fees: about $3.50 to $5.00, depending on your customers’ carriers</li>
<li>a local number: $1.15</li>
<li>10DLC: $1.50 to $10 a month for the use case, after $19.50 to $61 once</li>
</ul>
<p>That’s roughly $14.45 to $24.45 a month, before any replies.</p>
<p>With SMSTunnel, 1,000 texts a month is about 33 a day, more than the Free plan’s 25 as written, so you’d pick Starter: $9 a month, or $90 a year, plus your mobile plan. The <a href="https://smstunnel.io/blog/woocommerce-sms-without-twilio/">WooCommerce guide</a> shows the setup for an online shop.</p>
<h3 id="a-business-sending-10000-texts-a-month">A business sending 10,000 texts a month</h3>
<p>With Twilio, the texts alone come to $83, carrier fees add about $35 to $50, and the number and the registration add a few dollars more: roughly $120 to $145 a month.</p>
<p>With SMSTunnel, 10,000 texts a month is about 330 a day, past Starter’s 300, so Standard at $19 a month fits, plus your mobile plan. At this volume, check that your plan allows that many texts, and remember that one phone sends at its own pace (more on that below).</p>
<h3 id="sign-in-codes">Sign-in codes</h3>
<p>Twilio Verify sends codes and checks them for you: $0.05 per successful verification plus $0.0083 per text in the US. Vonage Verify charges $0.06084 per successful verification plus messaging rates, and Plivo Verify charges no verification fee, only the text. With SMSTunnel, your software makes the code and checks it, and SMSTunnel sends the text, with a separate allowance: 50 codes a day on Free and no daily cap on paid plans. The <a href="https://smstunnel.io/blog/sms-sign-in-codes-without-twilio/">sign-in codes guide</a> covers Supabase, Auth0, Clerk and Better Auth.</p>
<h2 id="other-tools-that-send-from-your-own-phone">Other tools that send from your own phone</h2>
<p>SMSTunnel isn’t the only way to send from an Android phone. <a href="https://smstunnel.io/compare/textbee/">textbee</a> has a free plan with 50 texts a day (300 a month) on one phone, where received texts count too, then Pro at $14.99 and Scale at $29.99 a month. <a href="https://smstunnel.io/compare/httpsms/">httpSMS</a> has 200 free texts a month, sent and received together, and paid plans from $10 to $350 a month. Like SMSTunnel, neither charges per text: your carrier’s charges apply. Both are open source and can run on your own server, and both install from an APK rather than from Google Play.</p>
<h2 id="when-an-sms-api-is-worth-the-price">When an SMS API is worth the price</h2>
<p>Our comparison pages are frank about this. Choose an SMS API provider when:</p>
<ul>
<li>you send high volumes: registered 10DLC numbers and short codes send many texts a second, while SMSTunnel sends what one phone can;</li>
<li>you need a short code, a toll-free number or a sender name instead of a phone number;</li>
<li>you text US customers as a business from a local number, where A2P 10DLC registration is the route US carriers set;</li>
<li>delivery mustn’t depend on a phone being on and online;</li>
<li>you need numbers in many countries without a SIM in each;</li>
<li>you want MMS, WhatsApp or RCS from the same API, or a service that checks sign-in codes for you;</li>
<li>you have no Android phone to keep on and online.</li>
</ul>
<p>You can also use both: everyday texts from your own number, and a provider for the rest.</p>
<h2 id="the-limits-of-one-phone">The limits of one phone</h2>
<p>A flat plan doesn’t turn a phone into a carrier. Keep these limits in mind:</p>
<ul>
<li><strong>Pace.</strong> The API takes 30 send requests a minute on each send endpoint.</li>
<li><strong>Daily caps.</strong> Free, Starter and Standard have daily caps. Professional and Enterprise don’t, but they’re rate-limited to protect your SIM.</li>
<li><strong>One recipient per text.</strong> Each person gets their own text.</li>
<li><strong>A phone that’s on.</strong> If your phone is offline, the text waits and goes out when it’s back, for up to 3 days; after that, it’s marked failed.</li>
<li><strong>Status.</strong> You get pending, sent or failed, as your phone reports it.</li>
</ul>
<p>If your app sends for many clients, each from their own phone and number, that’s the Enterprise plan: see <a href="https://smstunnel.io/blog/multi-tenant-sms-each-client-own-number/">one app, each client’s own number</a>.</p>
<h2 id="common-questions">Common questions</h2>
<h3 id="does-smstunnel-charge-per-message">Does SMSTunnel charge per message?</h3>
<p>No. The plans are flat, and your texts go out from your own SIM, so your carrier’s normal SMS rates or allowance apply.</p>
<h3 id="is-the-free-plan-enough-to-start">Is the Free plan enough to start?</h3>
<p>For testing and small volumes, yes, and it needs no credit card: 25 texts a day and 500 a month as you wrote them, plus 50 sign-in codes a day. Texts your own software sends beyond that, up to 25 more a day, end with a short “free by smstunnel.io” line. Paid plans add nothing to your texts.</p>
<h3 id="can-i-keep-my-sms-api-provider">Can I keep my SMS API provider?</h3>
<p>Yes. Texts sent through SMSTunnel come from the SIM in your phone, and your provider’s numbers keep working. You can send everyday texts from your own number and keep the provider for high volumes, short codes or other countries.</p>]]></content:encoded>
    </item>
    <item>
      <title>Send WooCommerce SMS from your own number, without Twilio</title>
      <link>https://smstunnel.io/blog/woocommerce-sms-without-twilio/</link>
      <guid isPermaLink="true">https://smstunnel.io/blog/woocommerce-sms-without-twilio/</guid>
      <pubDate>Tue, 03 Nov 2026 08:30:00 GMT</pubDate>
      <dc:creator>NARBOWEB SRL</dc:creator>
      <category>WooCommerce</category>
      <category>WordPress</category>
      <description>Text your WooCommerce customers about their orders from your own Android phone and number, with a free plugin: order statuses, tracking and new-order alerts.</description>
      <content:encoded><![CDATA[<p><a href="https://www.youtube.com/watch?v=0QQv9VDD5A8"><img src="https://smstunnel.io/video/woocommerce.webp" alt="WooCommerce SMS order notifications from your own phone" /></a></p><p>A short text is often the fastest way to tell customers that their order went through, that it has shipped, or that something needs their attention. The usual way to send them is an SMS API provider such as Twilio: you rent a number from the provider and pay for every text, and texting US customers from a local number also means registering it first (A2P 10DLC).</p>
<p>For many small shops there is a simpler route. Your WooCommerce store hands each order text to your SMSTunnel account, and the SMSTunnel app on your own Android phone sends it from your own number, on your own SIM and mobile plan. Customers see a number they can recognize and reply to, and SMSTunnel charges no per-message fees.</p>
<p>This guide sets it up from scratch with two free plugins from WordPress.org: the SMSTunnel core plugin, which links your site to your phone, and SMSTunnel for WooCommerce, which adds the order texts.</p>
<h2 id="how-it-works">How it works</h2>
<ol>
<li>Something happens in your shop: a customer places an order, or you move an order to Completed.</li>
<li>SMSTunnel for WooCommerce writes the text from your template and hands it to your SMSTunnel account.</li>
<li>The SMSTunnel app on your phone sends it as a normal SMS from your number.</li>
</ol>
<p>Each text goes to one number: the billing phone of that order. If your phone is offline for a while, the texts wait in your account and go out when it is back online, for up to 3 days.</p>
<h2 id="what-you-need">What you need</h2>
<ul>
<li>WordPress 5.0 or newer, PHP 7.4 or newer and WooCommerce 5.0 or newer</li>
<li>An Android phone (Android 7.0 or newer) with a SIM card, which stays on and online</li>
<li>The <a href="https://play.google.com/store/apps/details?id=io.smstunnel.app">SMSTunnel app from Google Play</a>, signed in to your SMSTunnel account (the Free plan needs no credit card)</li>
<li>The <a href="https://smstunnel.io/integrations/smstunnel/">SMSTunnel core plugin</a> and <a href="https://smstunnel.io/integrations/woocommerce/">SMSTunnel for WooCommerce</a>, both free on WordPress.org</li>
</ul>
<h2 id="step-1-link-your-site-to-your-phone">Step 1: Link your site to your phone</h2>
<p>Install the core plugin first. In your WordPress admin, go to <strong>Plugins → Add New</strong>, search for <strong>SMSTunnel</strong>, click <strong>Install Now</strong> and then <strong>Activate</strong>.</p>
<p>Then open <strong>SMSTunnel → Quick Setup</strong>. In the SMSTunnel app on your phone, open <strong>Connections → Link with QR code</strong>, scan the code and approve the connection on the phone. You can remove that connection in the app (<strong>Connections</strong>) at any time.</p>
<p>If you prefer a key, go to <strong>SMSTunnel → Settings</strong> and use <strong>Connect with API Key</strong>, with a key you create in your dashboard under <strong>Apps → Add an app</strong>.</p>
<h2 id="step-2-install-smstunnel-for-woocommerce">Step 2: Install SMSTunnel for WooCommerce</h2>
<p>Go to <strong>Plugins → Add New</strong> again, search for <strong>SMSTunnel for WooCommerce</strong>, click <strong>Install Now</strong> and <strong>Activate</strong>. Its settings live in <strong>WooCommerce → Settings → SMS</strong>.</p>
<p>The add-on works with WooCommerce’s High-Performance Order Storage (HPOS), and with both the Checkout block and the classic checkout.</p>
<h2 id="step-3-choose-which-order-statuses-send-a-text">Step 3: Choose which order statuses send a text</h2>
<p>The SMS tab has a switch for each text to the customer:</p>
<ul>
<li><strong>New Order (Pending)</strong>: the order still waits for payment, for example while the customer pays on another site</li>
<li><strong>Order Processing</strong>, <strong>Order Completed</strong>, <strong>Order On Hold</strong>, <strong>Order Cancelled</strong> and <strong>Order Refunded</strong>: sent each time an order moves to that status</li>
<li><strong>Shipment Tracking</strong>: a text with the tracking number (more on that below)</li>
</ul>
<p>New Order (Pending), Order Processing and Order Completed are on by default, and the add-on follows the tab from the start, even before you save it once. So check the tab right after you activate the plugin.</p>
<p>A customer who places an order gets one text at checkout, not two. If the order still waits for payment, it is the New Order (Pending) text. Otherwise it is the text for the status the order has at that moment, such as Order Processing for cash on delivery.</p>
<p><img src="https://smstunnel.io/screenshots/woocommerce/screen-1.png" alt="The SMS tab in the WooCommerce settings: a switch for each order status text, and the admin notifications" loading="lazy" decoding="async" width="1675" height="1841"></p>
<p>This screenshot comes from an earlier version, which still had a WooCommerce 2FA checkbox at the top. That checkbox did nothing and was removed in version 1.0.5: sign-in codes for the My Account login come from the core plugin (see the questions below).</p>
<h2 id="step-4-write-your-messages">Step 4: Write your messages</h2>
<p>Each text has its own template under <strong>SMS Message Templates</strong>, with these placeholders:</p>









































<table><thead><tr><th>Placeholder</th><th>What it becomes</th></tr></thead><tbody><tr><td><code>{order_id}</code></td><td>The order number</td></tr><tr><td><code>{order_total}</code></td><td>The order total, with the currency</td></tr><tr><td><code>{order_status}</code></td><td>The current status of the order</td></tr><tr><td><code>{customer_name}</code></td><td>The customer’s full name</td></tr><tr><td><code>{customer_first_name}</code></td><td>The customer’s first name</td></tr><tr><td><code>{site_name}</code></td><td>Your site’s name</td></tr><tr><td><code>{tracking_number}</code></td><td>The tracking number (Tracking Message only)</td></tr><tr><td><code>{tracking_provider}</code></td><td>The shipping company (Tracking Message only)</td></tr></tbody></table>
<p>The default New Order Message reads: “Hi {customer_first_name}, thank you for your order #{order_id} ({order_total}) at {site_name}. We’ll process it soon!” The default Completed Message: “Your order #{order_id} has been completed. Thank you for shopping at {site_name}!”</p>
<p>Keep them short and useful, and in your customers’ language. A text that says what happened and what comes next works better than a long one.</p>
<p><img src="https://smstunnel.io/screenshots/woocommerce/screen-2.png" alt="SMS Message Templates in WooCommerce: an editable message for each order status, for tracking and for the admin&#x27;s new order text" loading="lazy" decoding="async" width="1371" height="1744"></p>
<h2 id="texts-to-you-new-orders-and-low-stock">Texts to you: new orders and low stock</h2>
<p>The same tab has notifications for the shop itself. Put your own number in <strong>Admin Phone Number</strong> (one number, with + and the country code), then turn on:</p>
<ul>
<li><strong>Notify Admin on New Order</strong>: a text to you once for every order placed at checkout, even when the customer’s text is off or the customer gave no phone number</li>
<li><strong>Low Stock Alert</strong>: a text when a product runs low or out of stock</li>
</ul>
<h2 id="tracking-numbers">Tracking numbers</h2>
<p>The Shipment Tracking text goes out when you add a tracking number with the free Advanced Shipment Tracking plugin, once per tracking number. Other tracking plugins don’t tell other plugins when they add a number, so they aren’t supported directly.</p>
<p>Developers can send the tracking text from their own code by firing the action the add-on listens to:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="php"><code><span class="line"><span style="color:#B392F0">do_action</span><span style="color:#E1E4E8">( </span><span style="color:#9ECBFF">'woocommerce_shipment_tracking_number_added'</span><span style="color:#E1E4E8">, $order_id, $tracking_number, $tracking_provider );</span></span></code></pre>
<h2 id="ask-for-numbers-in-international-format">Ask for numbers in international format</h2>
<p>Customer texts go to the order’s billing phone, exactly as the customer typed it. The add-on doesn’t check or complete the number, and a number without + and the country code can reach the wrong country. Orders without a phone number get no text.</p>
<p>So say it clearly at checkout, in the field’s label or a short note: the number should start with + and the country code, like +44 7700 900123.</p>
<p>On the order page, a <strong>Send SMS</strong> box sends a one-off text to the customer, and every text sent on a status change is recorded in the order notes.</p>
<h2 id="how-many-texts-will-your-shop-send">How many texts will your shop send?</h2>
<p>Count about two or three texts per order (one at checkout, one when it’s completed, maybe one with the tracking number), plus one to you if you turned on the new order alert.</p>
<ul>
<li><strong>Free</strong>: 25 texts a day and 500 a month, sent exactly as you wrote them. Your site can send up to 25 more a day, each ending with a short “free by smstunnel.io” line.</li>
<li><strong>Starter</strong>, $9 a month: 300 texts a day and 5,000 a month.</li>
<li><strong>Standard</strong>, $19 a month: 2,000 texts a day and 30,000 a month.</li>
</ul>
<p>A shop with a few orders a day can stay on Free. One with ten orders a day and three texts per order sends about 30 texts a day, which fits Starter. Prices are before tax, and yearly plans cost ten months. The texts themselves go out over your own mobile plan, so your carrier’s SMS rates or allowance apply. Check that your plan allows this kind of use, and see <a href="https://smstunnel.io/pricing/">the pricing page</a> or our <a href="https://smstunnel.io/blog/sms-cost-own-sim-vs-sms-api/">cost comparison of your own SIM and an SMS API</a>.</p>
<h2 id="when-twilio-is-still-the-better-fit">When Twilio is still the better fit</h2>
<p>Your own phone works well for the texts a phone sends comfortably: order updates, reminders and sign-in codes. An SMS API provider fits better if you send at high volumes, need short codes or a sender name instead of a number, need registered US 10DLC numbers, or text customers in many countries.</p>
<p>When we compared them in November 2026, Twilio’s US pricing listed $0.0083 per text segment plus carrier fees, and $1.15 a month for a local number, plus the registration fees for US local numbers. The full side-by-side is on <a href="https://smstunnel.io/compare/twilio/">SMSTunnel vs Twilio</a>, and nothing stops you from using both.</p>
<h2 id="common-questions">Common questions</h2>
<h3 id="can-customers-reply">Can customers reply?</h3>
<p>Yes. The texts come from your own number, so replies reach your phone like any other text. If you turn on syncing of received texts in the app, you can also read and answer them on your computer.</p>
<h3 id="can-i-add-sms-sign-in-codes-to-the-my-account-login">Can I add SMS sign-in codes to the My Account login?</h3>
<p>Yes, with the core plugin: turn on <strong>WordPress Login 2FA</strong> in <strong>SMSTunnel → Settings</strong>, and it covers the WooCommerce My Account login too. Our guide to <a href="https://smstunnel.io/blog/sms-sign-in-codes-without-twilio/">SMS sign-in codes without Twilio</a> covers it, with other platforms.</p>
<h3 id="does-it-work-with-subscription-plugins">Does it work with subscription plugins?</h3>
<p>The status texts follow any WooCommerce order status change, including the ones that subscription plugins make.</p>
<h3 id="can-the-order-texts-be-encrypted">Can the order texts be encrypted?</h3>
<p>Yes, optionally. Turn on end-to-end encryption in the core plugin’s settings, and your site encrypts each text for your phone before it leaves WordPress, so SMSTunnel can’t read the words. An encrypted text counts toward your daily allowance and is refused past it, because it can’t carry the free line. See <a href="https://smstunnel.io/blog/end-to-end-encrypted-sms/">end-to-end encrypted SMS</a> for what is and isn’t encrypted.</p>]]></content:encoded>
    </item>
    <item>
      <title>SMS sign-in codes without Twilio: Supabase, Auth0, Clerk and Better Auth</title>
      <link>https://smstunnel.io/blog/sms-sign-in-codes-without-twilio/</link>
      <guid isPermaLink="true">https://smstunnel.io/blog/sms-sign-in-codes-without-twilio/</guid>
      <pubDate>Tue, 03 Nov 2026 08:20:00 GMT</pubDate>
      <dc:creator>NARBOWEB SRL</dc:creator>
      <category>Sign-in codes</category>
      <category>Auth</category>
      <description>Send the sign-in codes of Supabase, Auth0, Clerk and Better Auth from your own Android phone and number, with one small npm package and no SMS API account.</description>
      <content:encoded><![CDATA[<p><a href="https://www.youtube.com/watch?v=Rey_Kla6WkQ"><img src="https://smstunnel.io/video/setup-android.webp" alt="Set up SMSTunnel on your Android phone in 2 minutes" /></a></p><p>Phone sign-in codes are usually the first reason a project opens an account with an SMS API. You rent a number, register it where your country requires it, and pay for every text and every verification. For a product whose users mostly live in one country, there is a simpler route: send the codes from an Android phone you already have, on the plan you already pay for.</p>
<p>That is what SMSTunnel does. Your auth platform keeps creating and checking the codes exactly as it does today. A small package, <a href="https://www.npmjs.com/package/@narcisbodea/smstunnel-otp"><code>@narcisbodea/smstunnel-otp</code></a>, hands each code to your SMSTunnel account, and the SMSTunnel app on your phone sends it from its own SIM. People get their code from a number they know, and they can reply to it.</p>
<p>This guide covers the four platforms the package plugs into with a few lines of code (Supabase Auth, Auth0, Clerk and Better Auth), Ory, which needs no code at all, WordPress logins, and the limits you should know before you switch.</p>
<h2 id="how-it-works">How it works</h2>
<ol>
<li>A user asks for a code: they sign in with their phone number, add one, or confirm a second factor.</li>
<li>Your auth platform creates the code and calls your hook, Action, webhook or function, the place where it would normally call its SMS provider.</li>
<li>The package sends the text to SMSTunnel’s code endpoint, <a href="https://smstunnel.io/api-reference/#send-2fa"><code>POST /api/v1/sms/send-2fa</code></a>, with your API key.</li>
<li>Your SMSTunnel account hands the text to your phone, and the phone sends it from your own number.</li>
<li>The user types the code, and your auth platform checks it, as before.</li>
</ol>
<p>SMSTunnel never checks codes. Unlike Twilio Verify, it doesn’t create or verify them: your platform does that, and SMSTunnel only delivers the text. Codes also have their own daily allowance, separate from your other texts, so a busy sign-in morning doesn’t eat into your order updates or reminders.</p>
<p>The package has no runtime dependencies (it uses Fetch and WebCrypto only), so it runs in Node 18+, Deno (Supabase Edge Functions), Bun, Cloudflare Workers and Auth0 Actions.</p>
<h2 id="before-you-start">Before you start</h2>
<ol>
<li>Install the SMSTunnel app from Google Play on your Android phone and sign in. Sign in to your dashboard with the same account: the phone appears under <strong>Phones</strong>.</li>
<li>In the dashboard, open <strong>Apps → Add an app</strong>, pick <strong>Website or web app</strong>, name it and click <strong>Create the key</strong>. Copy the key right away, because it’s shown only once. Keep it in your platform’s secrets, never in code that runs in the browser.</li>
<li>Keep your codes between 4 and 10 digits. The default 6-digit codes of all the platforms below are fine; codes with letters aren’t accepted.</li>
</ol>
<p>Then install the package:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> install</span><span style="color:#9ECBFF"> @narcisbodea/smstunnel-otp</span></span></code></pre>
<h2 id="supabase-auth-the-send-sms-hook">Supabase Auth: the Send SMS hook</h2>
<p>Instead of its built-in SMS provider, Supabase can call an HTTPS endpoint with the user’s number and the code. The package gives you that endpoint, including the Standard Webhooks signature check. Create <code>supabase/functions/send-sms/index.ts</code>:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { createSupabaseSendSmsHook } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> 'npm:@narcisbodea/smstunnel-otp@^1.0.0'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8">Deno.</span><span style="color:#B392F0">serve</span><span style="color:#E1E4E8">(</span></span>
<span class="line"><span style="color:#B392F0">  createSupabaseSendSmsHook</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#E1E4E8">    apiKey: Deno.env.</span><span style="color:#B392F0">get</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'SMSTUNNEL_API_KEY'</span><span style="color:#E1E4E8">)</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">    hookSecret: Deno.env.</span><span style="color:#B392F0">get</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'SEND_SMS_HOOK_SECRETS'</span><span style="color:#E1E4E8">)</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// v1,whsec_...</span></span>
<span class="line"><span style="color:#E1E4E8">    template: </span><span style="color:#9ECBFF">'Your Acme code is {code}'</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// optional</span></span>
<span class="line"><span style="color:#E1E4E8">  }),</span></span>
<span class="line"><span style="color:#E1E4E8">);</span></span></code></pre>
<p>Deploy it without JWT checks, because the signature protects it:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">supabase</span><span style="color:#9ECBFF"> functions</span><span style="color:#9ECBFF"> deploy</span><span style="color:#9ECBFF"> send-sms</span><span style="color:#79B8FF"> --no-verify-jwt</span></span></code></pre>
<p>In the Supabase Dashboard, open <strong>Authentication → Hooks → Add a new hook → Send SMS hook</strong>, choose <strong>HTTPS</strong>, enter <code>https://&#x3C;project-ref>.supabase.co/functions/v1/send-sms</code>, then <strong>Generate secret</strong> and <strong>Create</strong>. Store both secrets:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">supabase</span><span style="color:#9ECBFF"> secrets</span><span style="color:#9ECBFF"> set</span><span style="color:#9ECBFF"> SMSTUNNEL_API_KEY=...</span><span style="color:#9ECBFF"> SEND_SMS_HOOK_SECRETS='v1,whsec_...'</span></span></code></pre>
<p>Finally, enable the provider under <strong>Authentication → Sign In / Providers → Phone</strong>. The SMS provider fields aren’t used while the hook is on. Keep phone auto-confirm off, or sign-ups never ask for a code.</p>
<p>The hook sends the code to the number Supabase gives it and adds the <code>+</code> that Supabase leaves out. If SMSTunnel is busy or out of reach, it answers 429 or 503 with <code>retry-after</code>, which Supabase retries within its five-second budget. The full walkthrough, with a config for local development, is on the <a href="https://smstunnel.io/integrations/supabase/">Supabase Auth page</a>.</p>
<h2 id="auth0-a-custom-phone-provider-action">Auth0: a Custom Phone Provider Action</h2>
<p>Auth0 can hand its phone messages to an Action. The <code>custom-phone-provider</code> trigger is the one Auth0 recommends: a single provider for MFA, passwordless SMS and phone sign-up.</p>
<ol>
<li>Open <strong>Branding → Phone Provider → Custom</strong> and pick the delivery method <strong>Text</strong>.</li>
<li>In the Action editor, add the dependency <code>@narcisbodea/smstunnel-otp</code> and the secret <code>SMSTUNNEL_API_KEY</code>, then paste:</li>
</ol>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="js"><code><span class="line"><span style="color:#F97583">const</span><span style="color:#E1E4E8"> { </span><span style="color:#79B8FF">createAuth0CustomPhoneProvider</span><span style="color:#E1E4E8"> } </span><span style="color:#F97583">=</span><span style="color:#B392F0"> require</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'@narcisbodea/smstunnel-otp'</span><span style="color:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> sendWithSmsTunnel</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> createAuth0CustomPhoneProvider</span><span style="color:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#79B8FF">exports</span><span style="color:#E1E4E8">.</span><span style="color:#B392F0">onExecuteCustomPhoneProvider</span><span style="color:#F97583"> =</span><span style="color:#F97583"> async</span><span style="color:#E1E4E8"> (</span><span style="color:#FFAB70">event</span><span style="color:#E1E4E8">, </span><span style="color:#FFAB70">api</span><span style="color:#E1E4E8">) </span><span style="color:#F97583">=></span><span style="color:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#F97583">  await</span><span style="color:#B392F0"> sendWithSmsTunnel</span><span style="color:#E1E4E8">(event, api);</span></span>
<span class="line"><span style="color:#E1E4E8">};</span></span></code></pre>
<ol start="3">
<li>Save, then use <strong>Send Test Message</strong>. For MFA, also turn on <strong>Use Tenant-Level Messaging Provider</strong> under <strong>Security → Multi-factor Auth → Phone Message</strong>.</li>
</ol>
<p>Codes go out with the text Auth0 rendered, in your template and the user’s language. Notices without a code, such as a blocked account or a breached password, are sent as regular texts, or ignored if you pass <code>{ nonCodeMessages: 'skip' }</code>. SMSTunnel sends text messages only, so a voice delivery is dropped with a clear reason. If you only need MFA, the older Send Phone Message trigger works too; both are on the <a href="https://smstunnel.io/integrations/auth0/">Auth0 page</a>.</p>
<h2 id="clerk-the-smscreated-webhook">Clerk: the sms.created webhook</h2>
<p>When you turn <strong>Delivered by Clerk</strong> off for an SMS template, Clerk stops sending that text and sends you an <code>sms.created</code> webhook instead. In Next.js, <code>app/api/webhooks/clerk-sms/route.ts</code> is all it takes:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { createClerkSmsWebhook } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '@narcisbodea/smstunnel-otp'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> const</span><span style="color:#79B8FF"> POST</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> createClerkSmsWebhook</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#E1E4E8">  apiKey: process.env.</span><span style="color:#79B8FF">SMSTUNNEL_API_KEY</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">  signingSecret: process.env.</span><span style="color:#79B8FF">CLERK_SMS_WEBHOOK_SIGNING_SECRET</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// whsec_...</span></span>
<span class="line"><span style="color:#E1E4E8">});</span></span></code></pre>
<ol>
<li>In the Clerk Dashboard, open <strong>Webhooks → Add Endpoint</strong>, enter your URL, subscribe to <code>sms.created</code> and copy the <strong>Signing Secret</strong>.</li>
<li>Under <strong>Customization → SMS</strong>, open each template you want sent from your phone (Verification code, Reset password code…) and turn <strong>Delivered by Clerk</strong> off. The setting is per template.</li>
<li>Keep the route public in your Clerk middleware: webhooks carry no session.</li>
</ol>
<p>The handler checks the Svix signature and sends the text Clerk rendered, making sure the code is in it. For Express and other frameworks, see the <a href="https://smstunnel.io/integrations/clerk/">Clerk page</a>.</p>
<h2 id="better-auth-the-phonenumber-plugin">Better Auth: the phoneNumber plugin</h2>
<p>Better Auth’s <code>phoneNumber</code> plugin asks you how to send its codes, and <code>smstunnelSendOTP()</code> answers:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { betterAuth } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> 'better-auth'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { phoneNumber } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> 'better-auth/plugins'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { smstunnelSendOTP } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '@narcisbodea/smstunnel-otp'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">const</span><span style="color:#79B8FF"> apiKey</span><span style="color:#F97583"> =</span><span style="color:#E1E4E8"> process.env.</span><span style="color:#79B8FF">SMSTUNNEL_API_KEY</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">export</span><span style="color:#F97583"> const</span><span style="color:#79B8FF"> auth</span><span style="color:#F97583"> =</span><span style="color:#B392F0"> betterAuth</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#E1E4E8">  plugins: [</span></span>
<span class="line"><span style="color:#B392F0">    phoneNumber</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#E1E4E8">      sendOTP: </span><span style="color:#B392F0">smstunnelSendOTP</span><span style="color:#E1E4E8">({ apiKey, template: </span><span style="color:#9ECBFF">'Your Acme code is {code}'</span><span style="color:#E1E4E8"> }),</span></span>
<span class="line"><span style="color:#E1E4E8">      sendPasswordResetOTP: </span><span style="color:#B392F0">smstunnelSendOTP</span><span style="color:#E1E4E8">({ apiKey, template: </span><span style="color:#9ECBFF">'Your Acme password reset code is {code}'</span><span style="color:#E1E4E8"> }),</span></span>
<span class="line"><span style="color:#E1E4E8">    }),</span></span>
<span class="line"><span style="color:#E1E4E8">  ],</span></span>
<span class="line"><span style="color:#E1E4E8">});</span></span></code></pre>
<p>Better Auth runs <code>sendOTP</code> in the background when it can, so sign-in doesn’t wait for the send. If people type local numbers, add a <code>defaultCountryCode</code> option, or check numbers with the plugin’s <code>phoneNumberValidator</code>. The other options are on the <a href="https://smstunnel.io/integrations/better-auth/">Better Auth page</a>.</p>
<h2 id="ory-wordpress-and-everything-else">Ory, WordPress and everything else</h2>
<p><strong>Ory</strong> (self-hosted Kratos or Ory Network) needs no package: its courier HTTP channel calls SMSTunnel directly, with a few lines of configuration. See the <a href="https://smstunnel.io/integrations/ory/">Ory page</a>.</p>
<p><strong>WordPress</strong> sites get sign-in codes from the <a href="https://smstunnel.io/integrations/smstunnel/">SMSTunnel plugin</a>: <strong>SMSTunnel → Settings → Two-Factor Authentication (2FA)</strong>, then <strong>WordPress Login 2FA</strong>. It also covers the WooCommerce My Account login, for the roles you choose, with codes valid for 5 minutes by default.</p>
<p><strong>Any other platform</strong> can use <code>sendCode()</code> from the same package:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { sendCode, SmsTunnelOtpError } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> '@narcisbodea/smstunnel-otp'</span><span style="color:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F97583">await</span><span style="color:#B392F0"> sendCode</span><span style="color:#E1E4E8">({</span></span>
<span class="line"><span style="color:#E1E4E8">  apiKey: process.env.</span><span style="color:#79B8FF">SMSTUNNEL_API_KEY</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">  to: </span><span style="color:#9ECBFF">'+14155550123'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">  code: </span><span style="color:#9ECBFF">'123456'</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#E1E4E8">  template: </span><span style="color:#9ECBFF">'Your Acme code is {code}'</span><span style="color:#E1E4E8">, </span><span style="color:#6A737D">// or message: 'full text'; the code is added if it is missing</span></span>
<span class="line"><span style="color:#6A737D">  // deviceId, defaultCountryCode, serverUrl, timeoutMs (default 10000)</span></span>
<span class="line"><span style="color:#E1E4E8">});</span></span></code></pre>
<p>When something goes wrong, it throws a <code>SmsTunnelOtpError</code> with the HTTP <code>status</code>, a <code>code</code> such as <code>RATE_LIMITED</code>, and <code>retryable: true</code> for the errors worth retrying.</p>
<h2 id="what-counts-as-a-code">What counts as a code</h2>
<p>The code endpoint refuses texts without a code, so your wording matters a little:</p>
<ul>
<li>A code is 4 to 10 digits standing on their own (<code>482913</code>, <code>Code: 1234</code>, <code>G-123456</code>), or two groups of 3 digits joined by a dash (<code>123-456</code>).</li>
<li>Not a code: codes with letters, numbers of 11 digits or more, digits right after a <code>+</code> (a phone number), and digits joined to others by <code>.</code>, <code>,</code>, <code>:</code> or <code>/</code>, such as prices, times and dates.</li>
<li>A text can be up to 320 characters. The package refuses a longer one before calling SMSTunnel.</li>
</ul>
<p>A text without a code is refused with a 400. Messages that aren’t codes go through the regular <a href="https://smstunnel.io/api-reference/#send-sms">send endpoint</a>.</p>
<h2 id="limits-and-when-an-sms-api-is-the-better-fit">Limits, and when an SMS API is the better fit</h2>
<p>Sending codes from a phone is not the same as renting a pipe from a carrier, and it’s worth knowing the difference before you switch:</p>
<ul>
<li><strong>One number per text.</strong> Every code goes to one person.</li>
<li><strong>30 sends a minute.</strong> Past that, SMSTunnel answers 429 and the package marks the error as retryable.</li>
<li><strong>Your phone has to be on and online.</strong> If it’s offline, the code waits and goes out as soon as the phone is back. A code that waits too long may expire before it arrives, so keep the phone charged and connected.</li>
<li><strong>Allowance.</strong> The Free plan includes 50 sign-in codes a day. From <a href="https://smstunnel.io/pricing/">Starter at $9 a month</a>, sign-in codes have no daily cap. Prices are before tax.</li>
<li><strong>Your carrier’s terms apply</strong>, because the texts go out over your own mobile plan.</li>
</ul>
<p>For comparison, Twilio’s pages listed Twilio Verify at $0.05 per successful verification plus $0.0083 per text in the US when we checked in November 2026, and prices differ by country. Twilio, or another SMS API provider, still fits better if you need high volumes, short codes, sender names, US 10DLC numbers or many countries. You can also use both: see <a href="https://smstunnel.io/compare/twilio/">SMSTunnel vs Twilio</a> and <a href="https://smstunnel.io/blog/sms-cost-own-sim-vs-sms-api/">what texts cost from your own SIM</a>.</p>
<h2 id="common-questions">Common questions</h2>
<h3 id="do-i-have-to-change-how-my-platform-makes-or-checks-codes">Do I have to change how my platform makes or checks codes?</h3>
<p>No. Supabase, Auth0, Clerk, Better Auth and Ory keep creating, storing and checking the codes. Only the delivery changes.</p>
<h3 id="can-i-write-the-text-myself">Can I write the text myself?</h3>
<p>Yes. Pass a <code>template</code> with <code>{code}</code> where the code goes. Without one, Auth0 and Clerk send the text they rendered from their own templates.</p>
<h3 id="can-i-send-codes-from-more-than-one-phone">Can I send codes from more than one phone?</h3>
<p>Every phone you link to your account can send. <code>sendCode()</code> and <code>smstunnelSendOTP()</code> take a <code>deviceId</code> to pick one, and the Auth0 Action reads it from the <code>SMSTUNNEL_DEVICE_ID</code> secret. Without one, your default phone sends.</p>
<h3 id="is-the-code-logged-anywhere">Is the code logged anywhere?</h3>
<p>Nothing in the package logs codes or keys. Each webhook handler also remembers recent webhook ids (per running instance), so a retried webhook doesn’t send the same code twice.</p>]]></content:encoded>
    </item>
    <item>
      <title>SMS for WordPress forms: Contact Form 7, WPForms, Gravity Forms, Fluent Forms and Forminator</title>
      <link>https://smstunnel.io/blog/sms-for-wordpress-forms/</link>
      <guid isPermaLink="true">https://smstunnel.io/blog/sms-for-wordpress-forms/</guid>
      <pubDate>Tue, 03 Nov 2026 08:10:00 GMT</pubDate>
      <dc:creator>NARBOWEB SRL</dc:creator>
      <category>WordPress</category>
      <category>Forms</category>
      <description>Get a text for each Contact Form 7, WPForms, Gravity Forms, Fluent Forms or Forminator submission, and confirm to the visitor, from your own phone and number.</description>
      <content:encoded><![CDATA[<p><a href="https://www.youtube.com/watch?v=UJRPg3IZBkM"><img src="https://smstunnel.io/video/wordpress-plugin.webp" alt="Send texts from WordPress through your own phone" /></a></p><p>A form on your WordPress site is often the first message a new customer sends you: a quote request, a booking, a question about a product. Most form plugins email it to you, and the email waits in an inbox until someone checks. A text lands on the phone that is already in your pocket.</p>
<p>The person who filled in the form wants to know it arrived, too. A short confirmation text, from a number they can call or reply to, does that better than a “thank you” page they close right away.</p>
<p>SMSTunnel does both for five form plugins: Contact Form 7, WPForms, Gravity Forms, Fluent Forms and Forminator. Your site hands each text to your SMSTunnel account, and the SMSTunnel app on your own Android phone sends it from your own number. This guide covers the setup common to all five, then each plugin’s own settings.</p>
<h2 id="two-texts-for-every-form">Two texts for every form</h2>
<p>Every integration below offers the same two texts, and you can use either one or both:</p>
<ul>
<li><strong>A text to you or your team</strong> when someone submits the form. You can list several numbers; each number gets its own text. Put the answers in it, such as the person’s name, phone and message.</li>
<li><strong>A confirmation to the person who filled in the form</strong>, at the number they entered in it. The add-ons call it SMS (2) or the autoresponder.</li>
</ul>
<p>The texts go out after each submission that passes the form’s own checks. If your phone is offline for a while, they wait and go out when it is back.</p>
<h2 id="first-link-your-site-to-your-phone">First: link your site to your phone</h2>
<p>All five need the free SMSTunnel core plugin, linked to your phone:</p>
<ol>
<li>Install the <a href="https://play.google.com/store/apps/details?id=io.smstunnel.app">SMSTunnel app from Google Play</a> on your Android phone and sign in.</li>
<li>In WordPress, go to <strong>Plugins → Add New</strong>, search for <strong>SMSTunnel</strong>, then <strong>Install Now</strong> and <strong>Activate</strong>.</li>
<li>Open <strong>SMSTunnel → Quick Setup</strong>. In the app, open <strong>Connections → Link with QR code</strong>, scan the code and approve the connection on your phone.</li>
</ol>
<p>You can also use <strong>Connect with API Key</strong> in <strong>SMSTunnel → Settings</strong>, as in the video above. Send yourself a test text from <strong>SMSTunnel → Send SMS</strong> before you go on: if it arrives, the rest is only form settings. More on the core plugin, including SMS sign-in codes for WordPress logins, is on the <a href="https://smstunnel.io/integrations/smstunnel/">core plugin page</a>.</p>
<h2 id="contact-form-7">Contact Form 7</h2>
<p>Install <strong>SMSTunnel for CF7</strong> from <strong>Plugins → Add New</strong>. Then go to <strong>Contact → Contact Forms</strong>, edit a form and open its <strong>SMS</strong> tab (shown to administrators).</p>
<ul>
<li>Tick <strong>Send SMS notification when this form is submitted</strong>, add your number(s) under <strong>Recipient Phone Number(s)</strong>, separated by commas, and write the <strong>Message Body</strong>.</li>
<li>For the confirmation, tick <strong>Use SMS (2)</strong>. Its <strong>Recipient Phone Number</strong> is <code>[your-phone]</code> by default: the visitor’s own phone field. Change the tag if your phone field has another name.</li>
</ul>
<p>The message can use any field of the form as a mail-tag, like <code>[your-name]</code> or <code>[your-message]</code>, plus <code>[_date]</code>, <code>[_time]</code>, <code>[_site_title]</code>, <code>[_site_url]</code>, <code>[_post_title]</code> and <code>[_post_url]</code>. Other special mail-tags are not replaced. A useful text to yourself:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="text"><code><span class="line"><span>New contact from [your-name]: [your-message] - Reply to: [your-phone]</span></span></code></pre>
<p><img src="https://smstunnel.io/screenshots/cf7/screen-1.png" alt="The SMS tab of a Contact Form 7 form: the text to you, with recipients and message, and SMS (2) for the confirmation to the visitor" loading="lazy" decoding="async" width="1758" height="1843"></p>
<p>All the details are on the <a href="https://smstunnel.io/integrations/contact-form-7/">Contact Form 7 page</a>.</p>
<h2 id="wpforms">WPForms</h2>
<p>Install <strong>SMSTunnel for WPForms</strong>. It is made for WPForms Lite, the free plugin, so you don’t need WPForms Pro. Open the form in the WPForms builder and go to <strong>Settings → SMS</strong>.</p>
<ul>
<li>Turn on <strong>Enable SMS notification when form is submitted</strong>, add the <strong>Recipient Phone Number(s)</strong> and edit the <strong>Message Body</strong>. By default it reads “New form submission from <code>{field_id="1"}</code>”, followed by every answer with <code>{all_fields}</code>.</li>
<li>Turn on <strong>Enable SMS (2) Autoresponder</strong>, put the phone field’s smart tag in <strong>Recipient Phone Number</strong>, like <code>{field_id="3"}</code>, and write the <strong>Confirmation Message</strong>.</li>
</ul>
<p>Besides <code>{field_id="X"}</code> and <code>{all_fields}</code>, the smart tags <code>{site_name}</code>, <code>{site_url}</code>, <code>{admin_email}</code>, <code>{user_ip}</code>, <code>{entry_id}</code> and <code>{date format="m/d/Y"}</code> work. Some other WPForms smart tags, like <code>{form_name}</code> and <code>{page_title}</code>, are not replaced. See the <a href="https://smstunnel.io/integrations/wpforms/">WPForms page</a>.</p>
<h2 id="gravity-forms">Gravity Forms</h2>
<p>Install <strong>SMSTunnel Notifications for Gravity Forms</strong>, edit a form and open <strong>Settings → SMS</strong>. Each form has its own SMS settings:</p>
<ul>
<li><strong>Enable</strong> sends a text when the form is submitted, to the <strong>Recipient Phone Number(s)</strong>. A merge tag works there too, like <code>{Phone:5}</code>.</li>
<li>The <strong>Message Body</strong> starts as “New form submission:” followed by <code>{all_fields}</code>. Every Gravity Forms merge tag works, like <code>{Name (First):1.3}</code> or <code>{entry_id}</code>.</li>
<li><strong>SMS (2) - Autoresponder</strong> has its own Enable box, one Recipient Phone Number and a Confirmation Message.</li>
</ul>
<p>Click <strong>Save SMS Settings</strong> when you’re done. There is no conditional logic: the texts go out after every submission. More on the <a href="https://smstunnel.io/integrations/gravity-forms/">Gravity Forms page</a>.</p>
<p><img src="https://smstunnel.io/screenshots/gravity/screen-2.png" alt="The SMS settings of a Gravity Forms form: the SMS Notification to you and the SMS (2) Autoresponder, with the form&#x27;s merge tags" loading="lazy" decoding="async" width="1780" height="1835"></p>
<h2 id="fluent-forms-built-into-smstunnel">Fluent Forms: built into SMSTunnel</h2>
<p>For Fluent Forms you don’t install anything else: since version 1.0.13, the core plugin adds its texts as soon as Fluent Forms is active, and it works with the free version.</p>
<p>Open your form and go to <strong>Settings &#x26; Integrations → Configure Integrations → Add New Integration</strong>. Pick <strong>SMSTunnel SMS</strong> (the text to you) or <strong>SMSTunnel SMS (2) Autoresponder</strong> (the confirmation). Give it a name, enter the numbers or pick the form’s phone field, like <code>{inputs.phone}</code>, write the message and click <strong>Save Feed</strong>.</p>
<p>Use the smartcodes next to the message, like <code>{inputs.name}</code>; <code>{all_data}</code> adds every answer as “Label: value”, one per line. Each text has an <strong>Enable this text</strong> switch, and you can switch off SMSTunnel texts for all forms in <strong>Fluent Forms → Integrations</strong>. See the <a href="https://smstunnel.io/integrations/fluent-forms/">Fluent Forms page</a>.</p>
<p><img src="https://smstunnel.io/fluent-forms-smstunnel-autoresponder.png" alt="The SMSTunnel SMS (2) Autoresponder in a Fluent Forms form: a name, the visitor&#x27;s phone field and the confirmation message" loading="lazy" decoding="async" width="1360" height="940"></p>
<h2 id="forminator-built-in-too">Forminator: built in too</h2>
<p>Forminator also works with the core plugin alone, free version included. Activate SMSTunnel once in <strong>Forminator → Integrations</strong>: find it under Available Apps, click <strong>+</strong> and <strong>Activate</strong>.</p>
<p>Then open your form, go to its <strong>Integrations</strong> tab and click <strong>+</strong> next to SMSTunnel. Give the text a name, choose <strong>Who gets this text?</strong> (SMSTunnel SMS or SMSTunnel SMS (2) Autoresponder), click <strong>Next</strong>, enter the numbers or pick the form’s phone field, write the message and <strong>Save</strong>. A form can have several texts.</p>
<p>The message uses the form’s field tags, like <code>{name-1}</code> or <code>{textarea-1}</code>, and <code>{all_fields}</code>, <code>{all_non_empty_fields}</code>, <code>{form_name}</code> and <code>{submission_id}</code>. In the form’s <strong>Phone</strong> field, set <strong>Validation</strong> to International. Details on the <a href="https://smstunnel.io/integrations/forminator/">Forminator page</a>.</p>
<p><img src="https://smstunnel.io/forminator-smstunnel-setup.png" alt="The first step of an SMSTunnel text in a Forminator form: a name and who gets the text" loading="lazy" decoding="async" width="1360" height="900"></p>
<h2 id="phone-numbers-the-one-rule-that-matters">Phone numbers: the one rule that matters</h2>
<p>Every number needs + and the country code, like +44 7700 900123. Ask for that format in your form’s phone field, in the label or the placeholder, because the plugins don’t add the country code for you:</p>
<ul>
<li>Contact Form 7, WPForms and Gravity Forms accept spaces, skip a local number that starts with 0, and read a number without + as if it began with a country code.</li>
<li>Fluent Forms and Forminator skip numbers without the country code.</li>
<li>All of them always skip premium-rate numbers; Fluent Forms and Forminator skip short codes too.</li>
</ul>
<p>Texts longer than 1,600 characters are cut, so <code>{all_fields}</code> on a very long form may not fit.</p>
<h2 id="other-form-plugins">Other form plugins</h2>
<p>SMSTunnel add-ons for <a href="https://smstunnel.io/integrations/elementor-forms/">Elementor Forms</a> (the Elementor Pro Form widget), <a href="https://smstunnel.io/integrations/ninja-forms/">Ninja Forms</a> and <a href="https://smstunnel.io/integrations/formidable-forms/">Formidable Forms</a> are coming to WordPress.org. Each page says how to get one sooner. For a WooCommerce shop, see <a href="https://smstunnel.io/blog/woocommerce-sms-without-twilio/">WooCommerce SMS from your own number</a>.</p>
<h2 id="common-questions">Common questions</h2>
<h3 id="do-i-pay-per-text">Do I pay per text?</h3>
<p>Not to SMSTunnel. The texts go out from your own SIM, so your mobile plan’s SMS rates or allowance apply. The Free plan covers 25 texts a day and 500 a month as written, and paid plans start at $9 a month; see <a href="https://smstunnel.io/pricing/">pricing</a>.</p>
<h3 id="can-i-text-everyone-who-ever-filled-in-my-form">Can I text everyone who ever filled in my form?</h3>
<p>That’s not what these integrations do. Each submission sends its own texts: one to you or your team, and one confirmation to the person who just filled in the form. Text only people who expect to hear from you.</p>
<h3 id="can-the-texts-be-encrypted">Can the texts be encrypted?</h3>
<p>Yes, optionally. Turn on end-to-end encryption in the core plugin’s settings, and your site encrypts its texts for your phone before they leave WordPress. The <a href="https://smstunnel.io/blog/end-to-end-encrypted-sms/">end-to-end encryption guide</a> explains what SMSTunnel can and can’t see.</p>]]></content:encoded>
    </item>
    <item>
      <title>Send texts from n8n through your own Android phone</title>
      <link>https://smstunnel.io/blog/n8n-send-sms-from-your-android-phone/</link>
      <guid isPermaLink="true">https://smstunnel.io/blog/n8n-send-sms-from-your-android-phone/</guid>
      <pubDate>Tue, 03 Nov 2026 08:00:00 GMT</pubDate>
      <dc:creator>NARBOWEB SRL</dc:creator>
      <category>n8n</category>
      <category>Automation</category>
      <description>Send SMS from your n8n workflows through your own Android phone and number with the free SMSTunnel community node: install, credentials and three workflows.</description>
      <content:encoded><![CDATA[<p><a href="https://www.youtube.com/watch?v=6TPAxJPe0bU"><img src="https://smstunnel.io/video/n8n.webp" alt="Send texts from n8n through your own phone" /></a></p><p>n8n is good at noticing things: a form was submitted, a row changed in a sheet, a workflow failed at 3 a.m., tomorrow’s appointments are ready. Telling a person about it by text message usually means signing up with an SMS API provider, renting a number and paying for every message.</p>
<p>If you have an Android phone with a SIM, there is a shorter route. The SMSTunnel community node for n8n hands each text to your SMSTunnel account, and the SMSTunnel app on your phone sends it from your own number. The people you text see a number they already know, and they can reply to it. SMSTunnel charges no per-message fees: what a text costs is whatever your mobile plan charges for SMS.</p>
<p>This guide sets it up from scratch on the Free plan: link the phone, create a key, install <code>n8n-nodes-smstunnel</code>, and build three workflows you can copy — a form that texts the person who filled it in, an alert when any workflow fails, and reminders the day before an appointment.</p>
<h2 id="what-you-need">What you need</h2>
<ul>
<li><strong>An Android phone</strong> with Android 7.0 or newer and a SIM that can send SMS, online over Wi-Fi or mobile data. A spare phone that stays on the charger works too.</li>
<li><strong>A free SMSTunnel account.</strong> The Free plan includes 25 texts a day and 500 a month sent exactly as you wrote them, up to 25 more texts a day from your own software that end with a short “free by smstunnel.io” line, and 50 sign-in codes a day. Paid plans start at $9 a month; see <a href="https://smstunnel.io/pricing/">pricing</a>.</li>
<li><strong>An n8n where you can install community nodes.</strong> This guide and the video use a self-hosted n8n. Where else community nodes can be installed is up to n8n, so check <a href="https://docs.n8n.io/integrations/community-nodes/">n8n’s page on community nodes</a>. If you can’t install them, the <a href="#no-community-nodes-use-the-http-request-node">HTTP Request node</a> does the same job with a few more fields.</li>
</ul>
<h2 id="step-1-link-your-phone">Step 1: link your phone</h2>
<ol>
<li>Create an account at <a href="https://dashboard.smstunnel.io/register">dashboard.smstunnel.io</a>, or continue with Google or Facebook.</li>
<li>Install the <a href="https://play.google.com/store/apps/details?id=io.smstunnel.app">SMSTunnel app from Google Play</a> on the phone, sign in with the same account and turn on the service. The app explains why it needs to send SMS before Android asks for the permission.</li>
<li>The phone now shows up in your dashboard under <strong>Phones</strong>. You can also link it with a QR code: open <strong>Phones → Add phone</strong> on your computer and scan the code from the app, under <strong>Connections → Link with QR code</strong>.</li>
</ol>
<p>Before you touch n8n, send yourself a test from the dashboard with <strong>Write a text</strong>. If it arrives, the rest is n8n configuration. The <a href="https://smstunnel.io/docs/">documentation</a> covers the app’s permissions and background service.</p>
<h2 id="step-2-create-a-key-for-n8n">Step 2: create a key for n8n</h2>
<p>In the dashboard, open <strong>Apps → Add an app</strong>, choose <strong>n8n</strong> and copy the key. It is shown only once, so paste it straight into n8n or somewhere safe. The key can send texts and check whether they went out, nothing more. To cut n8n off later, delete the app in the same place.</p>
<p>One operation needs more than that. <strong>Get Received</strong> reads the texts your phone receives, so it only works with a key you explicitly allowed to read them (<strong>Can also read received texts</strong> when you create it, or <strong>Allow reading received texts</strong> later). It also needs <strong>Sync received texts</strong> turned on in the app. Sync is off by default, only texts that arrive after you turn it on are copied, and your existing inbox is never read.</p>
<h2 id="step-3-install-n8n-nodes-smstunnel">Step 3: install n8n-nodes-smstunnel</h2>
<p>In n8n:</p>
<ol>
<li>Open <strong>Settings → Community Nodes</strong>.</li>
<li>Select <strong>Install</strong> and enter <code>n8n-nodes-smstunnel</code>.</li>
<li>Agree to the risks of using community nodes and select <strong>Install</strong>.</li>
</ol>
<p>To install it by hand instead, run this in your n8n installation directory:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">npm</span><span style="color:#9ECBFF"> install</span><span style="color:#9ECBFF"> n8n-nodes-smstunnel</span></span></code></pre>
<p>With Docker, add the package to your custom image or mount it into <code>~/.n8n/custom</code>. The node is open source, under the MIT license.</p>
<h2 id="step-4-add-the-credentials">Step 4: add the credentials</h2>
<p>Create new <strong>SMSTunnel API</strong> credentials:</p>

















<table><thead><tr><th>Field</th><th>Value</th></tr></thead><tbody><tr><td>API Key</td><td>The key from step 2. n8n sends it in the <code>X-API-Key</code> header.</td></tr><tr><td>Base URL</td><td><code>https://smstunnel.io</code>. Change it only for a self-hosted instance.</td></tr></tbody></table>
<p>Select <strong>Test</strong> before you save: it checks the key.</p>
<h2 id="step-5-text-the-person-who-filled-in-a-form">Step 5: text the person who filled in a form</h2>
<p>This is the workflow from the video: someone fills in a form and gets a text from your number seconds later.</p>
<ol>
<li>Add an <strong>n8n Form Trigger</strong> with two fields, <code>Name</code> and <code>Phone</code>.</li>
<li>Add the <strong>SMSTunnel</strong> node with resource <strong>Message</strong> and operation <strong>Send</strong>.</li>
<li>Fill in <strong>To</strong> and <strong>Message</strong> with expressions:</li>
</ol>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="text"><code><span class="line"><span>To:      {{ $json["Phone"] }}</span></span>
<span class="line"><span>Message: Thanks {{ $json["Name"] }}, we got your request and will call you back today.</span></span></code></pre>
<ol start="4">
<li>Under <strong>Additional Fields</strong>, set <strong>Default Country Code</strong>, for example <code>+44</code>. Numbers typed without <code>+</code> or <code>00</code> get that code and lose their leading 0, so <code>07700 900123</code> goes to <code>+447700900123</code>. It takes precedence over the country set for your phone.</li>
<li>Open the form, enter your own number and submit. The text arrives from the phone you linked.</li>
</ol>
<p>Every text the node sends has exactly one recipient. If ten people fill in the form, n8n runs the node ten times and each person gets their own text.</p>
<h2 id="two-more-workflows-worth-copying">Two more workflows worth copying</h2>
<h3 id="a-text-when-any-workflow-fails">A text when any workflow fails</h3>
<p>An SMS reaches you even when the phone in your pocket has no data connection.</p>
<ol>
<li>Create a workflow that starts with an <strong>Error Trigger</strong> node.</li>
<li>Add <strong>SMSTunnel → Message → Send</strong>, with <strong>To</strong> set to your own number and this <strong>Message</strong>:</li>
</ol>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="text"><code><span class="line"><span>Workflow {{$json["workflow"]["name"]}} failed: {{$json["execution"]["error"]["message"]}}</span></span></code></pre>
<ol start="3">
<li>In the settings of each workflow you want to watch, choose this one as its error workflow.</li>
</ol>
<h3 id="reminders-the-day-before-an-appointment">Reminders the day before an appointment</h3>
<p>A <strong>Schedule Trigger</strong> runs every day at 18:00. A database, Google Sheets or HTTP Request node fetches tomorrow’s appointments, and <strong>SMSTunnel → Send</strong> texts each person, with <strong>To</strong> set to <code>{{ $json.phone }}</code>. With <strong>Default Country Code</strong> set, numbers stored without a prefix still work. Because the reminder comes from your number, people can simply reply to move or cancel, and the reply lands on your phone. If your appointments already live in a spreadsheet or a calendar, the <a href="https://smstunnel.io/blog/sms-from-google-sheets-and-calendar/">Google Sheets and Calendar guide</a> shows another way to send them.</p>
<h2 id="sign-in-codes-status-and-replies">Sign-in codes, status and replies</h2>
<p>The node does more than send:</p>
<ul>
<li><strong>Send 2FA Code</strong> sends a sign-in code that your workflow generated. The text must contain the code — 4 to 10 digits standing on their own, such as <code>482913</code> or <code>12-345678</code>, or two groups of three digits like <code>123-456</code> — and can be up to 320 characters. Codes have their own daily allowance: 50 a day on the Free plan, no daily cap on paid plans. SMSTunnel only delivers the code; your workflow creates it and checks it. There is more in the guide to <a href="https://smstunnel.io/blog/sms-sign-in-codes-without-twilio/">sign-in codes without Twilio</a>.</li>
<li><strong>Get Status</strong> takes a message ID and tells you whether the text is pending, sent or failed. A failed text marked as unconfirmed was most likely sent, so don’t resend it automatically.</li>
<li><strong>Get Received</strong> returns the texts your phone received, with the permission and the sync from step 2. A scheduled workflow can use it to pick up replies.</li>
<li><strong>Get Usage</strong> shows what you sent today and what is left, and <strong>Device → Get Many</strong> lists your phones and which of them are online.</li>
</ul>
<h2 id="texting-a-few-people-at-once">Texting a few people at once</h2>
<p>Sometimes the same text should reach a handful of people, such as everyone on call this week. <strong>Send to Several Recipients</strong> takes up to 100 numbers separated by commas, semicolons or new lines, and each recipient gets their own text. It needs the phone to be online when it runs. A plain <strong>Send</strong> to an offline phone waits instead, and goes out when the phone is back, within 3 days.</p>
<p>Use it rather than a loop of <strong>Send</strong>: SMSTunnel accepts 30 send requests a minute, and the node doesn’t retry after a “too many requests” answer. Also worth knowing:</p>
<ul>
<li>A text can be up to 1600 characters, which the phone sends as up to 10 joined SMS.</li>
<li>A failed call stops the workflow. Turn on <strong>Continue On Fail</strong> to get an <code>error</code> field on the item instead, and handle it in the next node.</li>
</ul>
<p>Keep it to people who expect to hear from you: alerts, reminders, replies and codes.</p>
<h2 id="no-community-nodes-use-the-http-request-node">No community nodes? Use the HTTP Request node</h2>
<p>If your n8n can’t install community nodes, the built-in <strong>HTTP Request</strong> node can call the SMSTunnel API directly:</p>
<ul>
<li>method <code>POST</code> and URL <code>https://smstunnel.io/api/v1/sms/send</code>;</li>
<li>a header <code>X-API-Key</code> with your key, kept in an n8n credential such as <strong>Header Auth</strong> rather than typed into the node;</li>
<li>a JSON body with one recipient:</li>
</ul>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="json"><code><span class="line"><span style="color:#E1E4E8">{</span></span>
<span class="line"><span style="color:#79B8FF">  "to"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"+447700900123"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#79B8FF">  "message"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"Your order #1042 is ready for pickup."</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>The answer contains a <code>messageId</code> for checking the status later. Optional fields: <code>deviceId</code> (which of your phones sends it) and <code>defaultCountryCode</code>. The <a href="https://smstunnel.io/api-reference/">API reference</a> lists every endpoint and error, and the <a href="https://smstunnel.io/integrations/n8n/">n8n integration page</a> has all the node’s fields.</p>
<h2 id="common-questions">Common questions</h2>
<h3 id="does-it-work-on-the-free-plan">Does it work on the Free plan?</h3>
<p>Yes. The node is free, and on the Free plan up to 25 texts a day and 500 a month go out exactly as written; up to 25 more a day end with the short “free by smstunnel.io” line. Paid plans never add anything to your texts.</p>
<h3 id="whose-number-do-people-see">Whose number do people see?</h3>
<p>The number of the SIM in the phone that sends. Replies go to that phone like any other text, and with sync turned on you can also read them in your dashboard.</p>
<h3 id="what-if-my-phone-is-offline">What if my phone is offline?</h3>
<p>A text from <strong>Send</strong> waits in your account and goes out when the phone is back online, for up to 3 days. <strong>Send to Several Recipients</strong> needs the phone online.</p>]]></content:encoded>
    </item>
    <item>
      <title>Home Assistant SMS notifications from your own phone</title>
      <link>https://smstunnel.io/blog/home-assistant-sms-notifications/</link>
      <guid isPermaLink="true">https://smstunnel.io/blog/home-assistant-sms-notifications/</guid>
      <pubDate>Tue, 03 Nov 2026 07:50:00 GMT</pubDate>
      <dc:creator>NARBOWEB SRL</dc:creator>
      <category>Home Assistant</category>
      <category>Smart home</category>
      <description>Text yourself from Home Assistant through your own Android phone and number: leak, power cut, freezer and door alerts with notify.smstunnel, step by step.</description>
      <content:encoded><![CDATA[<p><a href="https://www.youtube.com/watch?v=X4G_Mr8LYzQ"><img src="https://smstunnel.io/video/home-assistant.webp" alt="Send texts from Home Assistant through your own phone" /></a></p><p>Push notifications from Home Assistant are great until the moment they matter. The phone you carry has no data connection, battery saver is on, or Android quietly killed the companion app, and the alert about the leak under the sink never shows up. A text message doesn’t need any of that: it reaches the phone in your pocket over the mobile network, with or without mobile data.</p>
<p>The SMSTunnel integration gives Home Assistant a <code>notify.smstunnel</code> service. Your automations hand each alert to your SMSTunnel account, and the SMSTunnel app on your Android phone sends it as an ordinary SMS, from your own number, on your own SIM. There is no third-party SMS provider in between and no per-message fee from SMSTunnel; your mobile plan’s SMS rates apply.</p>
<p>This guide goes from zero to four working automations — a water leak, a power cut, a freezer warming up and a door opening while nobody is home — plus the settings that stop a twitchy sensor from texting you all night.</p>
<h2 id="how-the-pieces-fit-together">How the pieces fit together</h2>
<ul>
<li><strong>The phone that sends</strong> is an Android phone (Android 7.0 or newer) with a SIM and the SMSTunnel app, online over Wi-Fi or mobile data. A good fit is an old phone that stays at home on the charger: your alerts then reach the phone you carry as a plain text.</li>
<li><strong>Home Assistant</strong> hands the alert to your SMSTunnel account over the internet, and the app on the phone sends it.</li>
<li><strong>The recipients</strong> are the numbers you choose: yours, your partner’s, a neighbour who has a key. Each recipient gets their own text.</li>
</ul>
<p>One honest limit: Home Assistant and the sending phone both need an internet connection to hand the alert over. If you want texts during a home internet or power outage, give them a backup — mobile data on the phone, and a UPS (or a mobile-data router) for Home Assistant and your router.</p>
<h2 id="step-1-link-a-phone-and-create-a-key">Step 1: link a phone and create a key</h2>
<ol>
<li>Create an account at <a href="https://dashboard.smstunnel.io/register">dashboard.smstunnel.io</a>, or continue with Google or Facebook.</li>
<li>On the sending phone, install the <a href="https://play.google.com/store/apps/details?id=io.smstunnel.app">SMSTunnel app from Google Play</a>, sign in with the same account and turn on the service. The phone appears in your dashboard under <strong>Phones</strong>; you can also link it by scanning the QR code from <strong>Phones → Add phone</strong>.</li>
<li>In the dashboard, open <strong>Apps → Add an app</strong>, choose <strong>Home Assistant</strong> and copy the key. It is shown only once. Deleting the app later cuts Home Assistant off.</li>
</ol>
<p>Send yourself a test from the dashboard with <strong>Write a text</strong>. If it arrives, the phone side is done. The <a href="https://smstunnel.io/docs/">documentation</a> explains the app’s permissions and its background service.</p>
<h2 id="step-2-install-the-integration">Step 2: install the integration</h2>
<p>The recommended way is HACS:</p>
<ol>
<li>In HACS, open the three-dot menu and choose <strong>Custom repositories</strong>.</li>
<li>Add <code>https://github.com/narcisbodea/hass-smstunnel</code> with the category <strong>Integration</strong>.</li>
<li>Install <strong>SMSTunnel</strong>, then restart Home Assistant.</li>
</ol>
<p>Without HACS, copy the <code>custom_components/smstunnel</code> folder from that repository into your Home Assistant <code>config/custom_components/</code> folder and restart.</p>
<h2 id="step-3-add-smstunnel-in-home-assistant">Step 3: add SMSTunnel in Home Assistant</h2>
<p>Go to <strong>Settings → Devices &#x26; Services → Add Integration → SMSTunnel</strong> and fill in the form:</p>





























<table><thead><tr><th>Field</th><th>What to enter</th></tr></thead><tbody><tr><td>API key</td><td>The key from step 1. It is checked right away, so a wrong key fails now, not at the first real alert.</td></tr><tr><td>Recipients</td><td>One or more numbers, separated by commas, with <code>+</code> and the country code.</td></tr><tr><td>Server URL</td><td><code>https://smstunnel.io</code>, unless you host SMSTunnel yourself.</td></tr><tr><td>Default country code</td><td>For example <code>+44</code>, so numbers saved without a prefix still work.</td></tr><tr><td>Device ID</td><td>Optional: pins sending to one of your phones.</td></tr></tbody></table>
<p>These settings can’t be edited later. To change them, remove the integration and add it again.</p>
<p>To test, open <strong>Developer tools</strong>, call <code>notify.smstunnel</code> with a short message, and wait for the text on your phone. From then on, any automation or script can use it.</p>
<h2 id="four-automations-to-copy">Four automations to copy</h2>
<p>The examples use the same syntax as the integration’s README. Home Assistant 2024.8 and newer write the call as <code>action: notify.smstunnel</code> instead of <code>service: notify.smstunnel</code>; it is the same service. Replace the entity IDs with your own.</p>
<h3 id="a-water-leak">A water leak</h3>
<p>The classic case, where minutes cost real money:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="yaml"><code><span class="line"><span style="color:#85E89D">automation</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">  - </span><span style="color:#85E89D">alias</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"SMS on water leak"</span></span>
<span class="line"><span style="color:#85E89D">    trigger</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">      - </span><span style="color:#85E89D">platform</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">state</span></span>
<span class="line"><span style="color:#85E89D">        entity_id</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">binary_sensor.basement_water_leak</span></span>
<span class="line"><span style="color:#85E89D">        to</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"on"</span></span>
<span class="line"><span style="color:#85E89D">    action</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">      - </span><span style="color:#85E89D">service</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">notify.smstunnel</span></span>
<span class="line"><span style="color:#85E89D">        data</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#85E89D">          title</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"Home Assistant"</span></span>
<span class="line"><span style="color:#85E89D">          message</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"Water leak detected in the basement!"</span></span></code></pre>
<p>SMS has no separate title field, so the text arrives as “Home Assistant: Water leak detected in the basement!”.</p>
<h3 id="a-power-cut">A power cut</h3>
<p>A UPS integration reports when the power goes out. The entity name depends on your UPS; the <code>for:</code> line waits a minute so a short flicker doesn’t text you. Keep the router on the UPS too, or the alert can’t go out.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="yaml"><code><span class="line"><span style="color:#85E89D">automation</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">  - </span><span style="color:#85E89D">alias</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"SMS when the power goes out"</span></span>
<span class="line"><span style="color:#85E89D">    trigger</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">      - </span><span style="color:#85E89D">platform</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">state</span></span>
<span class="line"><span style="color:#85E89D">        entity_id</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">binary_sensor.ups_on_battery</span></span>
<span class="line"><span style="color:#85E89D">        to</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"on"</span></span>
<span class="line"><span style="color:#85E89D">        for</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"00:01:00"</span></span>
<span class="line"><span style="color:#85E89D">    action</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">      - </span><span style="color:#85E89D">service</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">notify.smstunnel</span></span>
<span class="line"><span style="color:#85E89D">        data</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#85E89D">          message</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"Power cut at home: the UPS is running on battery."</span></span></code></pre>
<h3 id="a-freezer-warming-up">A freezer warming up</h3>
<p>Hours of warning instead of a ruined freezer. The message includes the current temperature, and the delay at the end, together with <code>mode: single</code>, allows at most one text an hour:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="yaml"><code><span class="line"><span style="color:#85E89D">automation</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">  - </span><span style="color:#85E89D">alias</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"SMS when the freezer warms up"</span></span>
<span class="line"><span style="color:#85E89D">    mode</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">single</span></span>
<span class="line"><span style="color:#85E89D">    trigger</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">      - </span><span style="color:#85E89D">platform</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">numeric_state</span></span>
<span class="line"><span style="color:#85E89D">        entity_id</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">sensor.freezer_temperature</span></span>
<span class="line"><span style="color:#85E89D">        above</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">-12</span></span>
<span class="line"><span style="color:#85E89D">        for</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"00:15:00"</span></span>
<span class="line"><span style="color:#85E89D">    action</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">      - </span><span style="color:#85E89D">service</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">notify.smstunnel</span></span>
<span class="line"><span style="color:#85E89D">        data</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#85E89D">          message</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"Freezer at {{ states('sensor.freezer_temperature') }} °C for 15 minutes."</span></span>
<span class="line"><span style="color:#E1E4E8">      - </span><span style="color:#85E89D">delay</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"01:00:00"</span></span></code></pre>
<h3 id="the-front-door-opens-while-nobody-is-home">The front door opens while nobody is home</h3>
<p>This one goes to two people instead of the recipients you saved: <code>target</code> takes one number or a list, and replaces the saved recipients for that call only. The condition uses the home zone, whose state is the number of people at home.</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="yaml"><code><span class="line"><span style="color:#85E89D">automation</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">  - </span><span style="color:#85E89D">alias</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"SMS when the front door opens and nobody is home"</span></span>
<span class="line"><span style="color:#85E89D">    trigger</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">      - </span><span style="color:#85E89D">platform</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">state</span></span>
<span class="line"><span style="color:#85E89D">        entity_id</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">binary_sensor.front_door</span></span>
<span class="line"><span style="color:#85E89D">        to</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"on"</span></span>
<span class="line"><span style="color:#85E89D">    condition</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">      - </span><span style="color:#85E89D">condition</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">state</span></span>
<span class="line"><span style="color:#85E89D">        entity_id</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">zone.home</span></span>
<span class="line"><span style="color:#85E89D">        state</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"0"</span></span>
<span class="line"><span style="color:#85E89D">    action</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">      - </span><span style="color:#85E89D">service</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">notify.smstunnel</span></span>
<span class="line"><span style="color:#85E89D">        data</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#85E89D">          message</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"The front door just opened and nobody is home."</span></span>
<span class="line"><span style="color:#85E89D">          target</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#E1E4E8">            - </span><span style="color:#9ECBFF">"+447700900123"</span></span>
<span class="line"><span style="color:#E1E4E8">            - </span><span style="color:#9ECBFF">"+447700900456"</span></span></code></pre>
<p>For a single call you can also pick another phone or country code, under the service’s own <code>data</code>:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="yaml"><code><span class="line"><span style="color:#85E89D">service</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">notify.smstunnel</span></span>
<span class="line"><span style="color:#85E89D">data</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#85E89D">  message</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"Test from the phone in the garage"</span></span>
<span class="line"><span style="color:#85E89D">  data</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#85E89D">    device_id</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"your-phone-id"</span></span>
<span class="line"><span style="color:#85E89D">    default_country_code</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"+44"</span></span></code></pre>
<h2 id="keep-a-twitchy-sensor-from-texting-you-all-night">Keep a twitchy sensor from texting you all night</h2>
<p>A sensor that flips between on and off can fire an automation dozens of times in a few minutes, and every run is a text. That burns through your allowance and tries your carrier’s patience. Three habits prevent it:</p>
<ul>
<li><strong>Add <code>for:</code></strong> to the trigger, so the state must hold for a while before anything is sent.</li>
<li><strong>Add a condition</strong> that only lets the alert through when it matters, like the empty-house check above.</li>
<li><strong>Add a cooldown</strong> with <code>mode: single</code> and a <code>delay</code> at the end, as in the freezer example.</li>
</ul>
<p>The numbers to keep in mind: the Free plan sends 25 texts a day and 500 a month exactly as written, and up to 25 more a day end with a short “free by smstunnel.io” line. Paid plans start at $9 a month, with higher daily limits; see <a href="https://smstunnel.io/pricing/">pricing</a>. On every plan, SMSTunnel accepts 30 send requests a minute.</p>
<h2 id="when-something-goes-wrong">When something goes wrong</h2>
<p>The integration is built so that one problem doesn’t hide another:</p>
<ul>
<li><strong>Errors go to the Home Assistant log</strong>, not to the automation, so the rest of the automation still runs.</li>
<li><strong>One failed recipient doesn’t block the others</strong>: the remaining texts still go out.</li>
<li><strong>Past 30 requests a minute</strong>, the text is not sent and the log says so plainly. There is no automatic retry.</li>
<li><strong>Texts longer than 1600 characters are cut</strong> before sending, rather than refused.</li>
<li><strong>If the sending phone is offline</strong>, the text waits in your account and goes out when the phone is back.</li>
</ul>
<p>Removing the integration stops <code>notify.smstunnel</code> at once, without a restart, and deleting its app in the dashboard stops the key.</p>
<h2 id="common-questions">Common questions</h2>
<h3 id="which-number-do-the-alerts-come-from">Which number do the alerts come from?</h3>
<p>From the SIM in the phone that runs the SMSTunnel app. Recipients can save it as “Home” and recognise it at a glance.</p>
<h3 id="can-people-reply">Can people reply?</h3>
<p>Yes. Replies reach the sending phone like any other text, and with sync turned on in the app you can read them in your dashboard too. The integration itself only sends.</p>
<h3 id="does-home-assistant-need-to-be-reachable-from-the-internet">Does Home Assistant need to be reachable from the internet?</h3>
<p>The integration calls SMSTunnel from Home Assistant, so Home Assistant needs a way out to the internet. It doesn’t need to accept connections from outside.</p>
<h3 id="is-there-an-alternative-without-home-assistant">Is there an alternative without Home Assistant?</h3>
<p>For servers and monitoring tools, the guide to <a href="https://smstunnel.io/blog/monitoring-alerts-by-sms/">monitoring alerts by SMS</a> covers Uptime Kuma, Grafana and Zabbix. For other workflows, <a href="https://smstunnel.io/blog/n8n-send-sms-from-your-android-phone/">n8n</a> works the same way. Everything about the integration itself is on the <a href="https://smstunnel.io/integrations/home-assistant/">Home Assistant integration page</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Text reminders from Google Calendar and Google Sheets, sent from your own phone</title>
      <link>https://smstunnel.io/blog/sms-from-google-sheets-and-calendar/</link>
      <guid isPermaLink="true">https://smstunnel.io/blog/sms-from-google-sheets-and-calendar/</guid>
      <pubDate>Tue, 03 Nov 2026 07:40:00 GMT</pubDate>
      <dc:creator>NARBOWEB SRL</dc:creator>
      <category>Google Sheets</category>
      <category>Google Calendar</category>
      <description>Google Calendar stopped texting reminders in 2019. Bring them back, and text people from Google Sheets, with free Apps Script templates and your own phone.</description>
      <content:encoded><![CDATA[<p><a href="https://www.youtube.com/watch?v=Rey_Kla6WkQ"><img src="https://smstunnel.io/video/setup-android.webp" alt="Set up SMSTunnel on your Android phone in 2 minutes" /></a></p><p>Google Calendar stopped sending SMS notifications on 7 January 2019. Since then its reminders come as app and email notifications only, which are easy to miss when you are driving, in a meeting or away from your inbox. A text is harder to miss, and it also reaches people who don’t use Google Calendar at all, like the clients in your appointments.</p>
<p>SMSTunnel brings text reminders back, and adds texts from your spreadsheets. Free Google Apps Script templates hand each text to your SMSTunnel account, and your own Android phone sends it from your own number. The people you text see a number they know, and they can reply to you. There is no server to run: everything lives in Apps Script, inside your own spreadsheet.</p>
<h2 id="what-you-can-text-from-google">What you can text from Google</h2>
<p>There are four ready templates, plus a small library for your own code:</p>

























<table><thead><tr><th>Template</th><th>What it texts</th></tr></thead><tbody><tr><td>Google Calendar</td><td>Reminders before your events, a morning agenda, reminders to the people in your appointments</td></tr><tr><td>Google Sheets: status texts</td><td>A text to the row’s number when its status becomes “Shipped”, “Ready” or another value you choose</td></tr><tr><td>Google Sheets: appointment reminders</td><td>One reminder for each appointment, a few hours before</td></tr><tr><td>Google Forms</td><td>A text to you for each response, and an optional confirmation to the person who answered</td></tr></tbody></table>
<p>Every template sends one text per number, only to the numbers it was set up for, with a cap per run, and never the same text twice. There is no button that texts everyone in a sheet, on purpose.</p>
<h2 id="what-you-need">What you need</h2>
<ul>
<li>An Android phone with the <a href="https://play.google.com/store/apps/details?id=io.smstunnel.app">SMSTunnel app from Google Play</a>, signed in to your account. The video above shows the setup.</li>
<li>A Google account. Personal accounts and Google Workspace accounts both work, and Apps Script is free with either.</li>
<li>An SMSTunnel key. In your <a href="https://dashboard.smstunnel.io/apps">dashboard</a>, open <strong>Apps → Add an app</strong>, pick <strong>Script or server</strong>, name it (for example “Google Sheets”) and click <strong>Create the key</strong>. Copy it right away: it is shown only once. Make one key per spreadsheet or form, so you can stop one without touching the others.</li>
</ul>
<h2 id="step-1-add-a-template-to-your-spreadsheet">Step 1: add a template to your spreadsheet</h2>
<p>One-click copies of the templates (<strong>Make a copy</strong>) are coming soon. Today you paste the code from the <a href="https://github.com/narcisbodea/smstunnel-google">smstunnel-google repository on GitHub</a>, which takes a few minutes:</p>
<ol>
<li>Open the spreadsheet: your data for Google Sheets, or a new one for Google Calendar (it keeps the log and, if you want, your guests’ numbers). Then choose <strong>Extensions → Apps Script</strong>.</li>
<li>In the repository, open the template’s folder: <code>apps-script/calendar-reminders</code>, <code>apps-script/sheets-status-texts</code> or <code>apps-script/sheets-reminders</code>. Make the same files in the editor and paste each one’s content: <code>SmsTunnel</code>, <code>SmsTunnelKit</code> and <code>Code</code> as script files (<strong>+ → Script</strong>), and <code>Setup</code> as an HTML file (<strong>+ → HTML</strong>).</li>
<li>Open <strong>Project Settings</strong> (the gear), tick <strong>Show “appsscript.json” manifest file in editor</strong>, open <code>appsscript.json</code> and replace it with the template’s.</li>
<li>Save, go back to your spreadsheet and reload it. The <strong>SMSTunnel</strong> menu shows up after a few seconds.</li>
</ol>
<p>For a form, the editor is under <strong>⋮ (More) → Apps Script</strong> in the form’s editor, and the folder is <code>apps-script/forms-text-me</code>. People who fill in the form never see the menu.</p>
<h2 id="step-2-set-it-up-and-send-a-test">Step 2: set it up and send a test</h2>
<p>Choose <strong>SMSTunnel → Set up</strong>. The first time, Google asks you to allow the script and, because it is your own copy of a script Google hasn’t reviewed, shows <strong>“Google hasn’t verified this app”</strong>. That is expected: click <strong>Advanced → Go to … (unsafe)</strong>. Tick all the permissions: the script calls only smstunnel.io, works in this spreadsheet only, reads your calendars without ever changing them, and runs its triggers while you are away.</p>
<p>Then paste your key, click <strong>Check</strong>, choose the phone that sends the texts, fill in the rest and click <strong>Save and turn on</strong>. Finish with a test to yourself: <strong>SMSTunnel → Send a test text</strong>, or <strong>Send me a test reminder</strong> in the Calendar template.</p>
<h2 id="google-calendar-three-kinds-of-reminders">Google Calendar: three kinds of reminders</h2>
<p>Set up has three parts. Turn on the ones you want.</p>
<h3 id="remind-me-before-each-event">Remind me before each event</h3>
<p>A text to your own numbers (up to 5, one text each) 10, 15, 30 or 45 minutes, 1 to 12 hours, or a day before each event in the calendars you choose. You can limit it to events whose title has a word you choose, like “dentist”. All-day events are left out unless you include them. A reminder reads like this:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="text"><code><span class="line"><span>Reminder: Dentist at 2:30 PM (in 15 min)</span></span></code></pre>
<h3 id="your-agenda-every-morning">Your agenda every morning</h3>
<p>One text a day at the hour you choose, such as “Today, Mon, Nov 2: 9:00 AM Dentist, 2:00 PM Call with Ana”. Days with nothing planned are skipped unless you say otherwise.</p>
<h3 id="remind-the-people-in-your-appointments">Remind the people in your appointments</h3>
<p>This is the one for practices, salons and anyone who books clients. The number comes from a line in the event’s description that starts with a word you choose and a colon, such as <code>SMS: +1 415 555 0123</code>, or from a <strong>Phone numbers</strong> sheet for guests you invited. Other numbers in a description, such as a video call’s dial-in line, are never used.</p>
<p>By default only events you created count, so an invitation from someone else can’t choose who gets a text from your phone. Reminders to people go out only between the hours you choose (8:00 to 21:00 by default), to at most 10 people per event. Texts can use <code>{Title}</code>, <code>{Date}</code>, <code>{Time}</code>, <code>{Location}</code>, <code>{Calendar}</code>, <code>{In}</code> and, for people, <code>{Name}</code>.</p>
<p>Each reminder goes out once. A hidden <strong>SMSTunnel sent</strong> sheet records every text, and if an event moves, its new time gets one new text. <strong>Preview the next texts</strong> shows what would go out in the next 24 hours without sending anything.</p>
<h2 id="google-sheets-status-texts-and-appointment-reminders">Google Sheets: status texts and appointment reminders</h2>
<h3 id="a-text-when-a-rows-status-changes">A text when a row’s status changes</h3>
<p>Choose the sheet, the status column, the value that triggers the text and the phone column, then write the text with the row’s columns as placeholders:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="text"><code><span class="line"><span>Hi {Name}, your order {Order} has shipped.</span></span></code></pre>
<p>When someone sets a row’s status to that value, the number in that row gets the text, and a <strong>Texted at</strong> column shows when. A row is never texted twice, and rows that already had the value at setup are skipped, so only changes from now on send texts.</p>
<p>Pasting the value into many rows sends at most 20 texts at once (you can raise that to 50); the rest wait. Edits made by other apps or scripts don’t fire Google’s edit trigger, so tick <strong>Also check every 15 minutes</strong> if your sheet is filled in by another tool.</p>
<h3 id="appointment-reminders-from-a-sheet">Appointment reminders from a sheet</h3>
<p>Choose the phone column, the date column and, if the time has its own column, the time column. Dates can be real dates or text like <code>2026-11-02 14:30</code>, read in the spreadsheet’s time zone (<strong>File → Settings</strong>). Every hour, each row whose appointment falls within the next hours you choose (24 by default) gets one reminder, between 8:00 and 21:00 by default, at most 25 per run. <strong>Reminder sent</strong> records it, and a row without a valid number is marked <strong>Not sent</strong> with the reason.</p>
<h2 id="your-own-apps-script-code">Your own Apps Script code</h2>
<p>Developers can paste <code>apps-script/library/SmsTunnel.js</code> into any Apps Script project and call it directly. Save the key once with <code>SmsTunnel.setApiKey()</code>, which keeps it in your user properties, then send:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="js"><code><span class="line"><span style="color:#6A737D">// Text the number in the row you are on.</span></span>
<span class="line"><span style="color:#F97583">function</span><span style="color:#B392F0"> textThisRow</span><span style="color:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#F97583">  var</span><span style="color:#E1E4E8"> sheet </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> SpreadsheetApp.</span><span style="color:#B392F0">getActiveSheet</span><span style="color:#E1E4E8">();</span></span>
<span class="line"><span style="color:#F97583">  var</span><span style="color:#E1E4E8"> row </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> sheet.</span><span style="color:#B392F0">getActiveRange</span><span style="color:#E1E4E8">().</span><span style="color:#B392F0">getRow</span><span style="color:#E1E4E8">();</span></span>
<span class="line"><span style="color:#F97583">  var</span><span style="color:#E1E4E8"> phone </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> SmsTunnel.</span><span style="color:#B392F0">normalizePhone</span><span style="color:#E1E4E8">(sheet.</span><span style="color:#B392F0">getRange</span><span style="color:#E1E4E8">(row, </span><span style="color:#79B8FF">2</span><span style="color:#E1E4E8">).</span><span style="color:#B392F0">getDisplayValue</span><span style="color:#E1E4E8">(), </span><span style="color:#9ECBFF">'+1'</span><span style="color:#E1E4E8">);</span></span>
<span class="line"><span style="color:#F97583">  if</span><span style="color:#E1E4E8"> (</span><span style="color:#F97583">!</span><span style="color:#E1E4E8">phone.ok) </span><span style="color:#F97583">throw</span><span style="color:#F97583"> new</span><span style="color:#B392F0"> Error</span><span style="color:#E1E4E8">(phone.message);</span></span>
<span class="line"><span style="color:#F97583">  var</span><span style="color:#E1E4E8"> result </span><span style="color:#F97583">=</span><span style="color:#E1E4E8"> SmsTunnel.</span><span style="color:#B392F0">sendSms</span><span style="color:#E1E4E8">(phone.number, </span><span style="color:#9ECBFF">'Your order is ready for pickup.'</span><span style="color:#E1E4E8">);</span></span>
<span class="line"><span style="color:#E1E4E8">  sheet.</span><span style="color:#B392F0">getRange</span><span style="color:#E1E4E8">(row, </span><span style="color:#79B8FF">5</span><span style="color:#E1E4E8">).</span><span style="color:#B392F0">setValue</span><span style="color:#E1E4E8">(result.queued </span><span style="color:#F97583">?</span><span style="color:#9ECBFF"> 'Waiting for my phone'</span><span style="color:#F97583"> :</span><span style="color:#9ECBFF"> 'Texted'</span><span style="color:#E1E4E8">);</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>One number per call: to text several people, call <code>sendSms</code> once for each. The <a href="https://smstunnel.io/integrations/google-sheets/">Google Sheets guide</a> lists the other functions and their errors.</p>
<h2 id="limits-worth-knowing">Limits worth knowing</h2>
<ul>
<li><strong>Numbers</strong> go in international format, like <code>+14155550123</code>. <strong>Format → Number → Plain text</strong> keeps the <code>+</code> in a cell. A number without a country code is skipped unless you set one in <strong>Set up → More settings</strong>, so a text never goes to the wrong country.</li>
<li><strong>SMSTunnel</strong> takes 30 send requests a minute and up to 1600 characters per text, and your plan’s daily allowance applies. The templates pause 2 seconds between texts and cap each run.</li>
<li><strong>Google’s quotas</strong>: a free Google account gets 20,000 URL Fetch calls a day (one per text) and 90 minutes of trigger run time a day; Workspace accounts get more.</li>
<li><strong>If your phone is offline</strong>, the text waits in your account and goes out as soon as the phone is back online.</li>
<li><strong>Your key</strong> stays in the script’s user properties, never in a cell or a log. Anyone with edit access to the spreadsheet can edit its script, so share it only with people you trust. Deleting the key under <strong>Apps</strong> in your dashboard stops every text at once.</li>
</ul>
<h2 id="common-questions">Common questions</h2>
<h3 id="does-it-cost-anything">Does it cost anything?</h3>
<p>The templates are free, and so is Apps Script. The texts count against your SMSTunnel plan, and your mobile plan applies to the texts your phone sends. The <a href="https://smstunnel.io/pricing/">Free plan</a> covers 25 texts a day and 500 a month exactly as written, which is plenty for personal reminders; Starter, at $9 a month, allows 300 texts a day for a busy appointment book.</p>
<h3 id="whose-number-do-people-see">Whose number do people see?</h3>
<p>Yours. The texts leave your own phone, from its SIM, like a text you type yourself, and replies come back to your phone.</p>
<h3 id="my-calendar-is-in-outlook-what-then">My calendar is in Outlook. What then?</h3>
<p>See <a href="https://smstunnel.io/integrations/outlook-calendar/">reminder texts for Outlook Calendar</a>, built with n8n, Power Automate, Zapier or Make.</p>
<h3 id="can-an-ai-assistant-send-the-reminders-instead">Can an AI assistant send the reminders instead?</h3>
<p>Yes, when you ask: with the <a href="https://smstunnel.io/blog/mcp-server-sms-claude-cursor/">SMSTunnel MCP server</a> and a calendar connector, an assistant like Claude can text tomorrow’s clients after showing you the list. It doesn’t run on a schedule, though, so for reminders that go out on their own, use the Calendar template.</p>]]></content:encoded>
    </item>
    <item>
      <title>Monitoring alerts by SMS from your own phone: Uptime Kuma, Grafana, Zabbix and GitHub Actions</title>
      <link>https://smstunnel.io/blog/monitoring-alerts-by-sms/</link>
      <guid isPermaLink="true">https://smstunnel.io/blog/monitoring-alerts-by-sms/</guid>
      <pubDate>Tue, 03 Nov 2026 07:30:00 GMT</pubDate>
      <dc:creator>NARBOWEB SRL</dc:creator>
      <category>Monitoring</category>
      <category>Alerts</category>
      <description>Get Uptime Kuma, Grafana, Zabbix and GitHub Actions alerts as texts sent by your own Android phone, from your own number. No GSM modem, no SMS provider.</description>
      <content:encoded><![CDATA[<p><a href="https://www.youtube.com/watch?v=Rey_Kla6WkQ"><img src="https://smstunnel.io/video/setup-android.webp" alt="Set up SMSTunnel on your Android phone in 2 minutes" /></a></p><p>An alert only helps if it reaches someone. A text still gets through when an email or a push notification doesn’t, and it arrives from a number the on-call person knows and can call back.</p>
<p>With SMSTunnel, your monitoring tools hand each alert to your SMSTunnel account, and your own Android phone texts it from its own SIM. There is no GSM modem to plug into a server and no SMS provider in between: each alert is an ordinary text on your phone’s plan. This guide sets up four common tools, Uptime Kuma, Grafana, Zabbix and GitHub Actions, and a webhook address for everything else.</p>
<h2 id="before-you-start-one-key-per-tool">Before you start: one key per tool</h2>
<p>Install the <a href="https://play.google.com/store/apps/details?id=io.smstunnel.app">SMSTunnel app from Google Play</a> on an Android phone and sign in; the video above takes you through it. Then, in your <a href="https://dashboard.smstunnel.io/apps">dashboard</a>, open <strong>Apps → Add an app</strong>, pick <strong>Script or server</strong>, name it after the tool (for example “Uptime Kuma”) and click <strong>Create the key</strong>. Copy the key right away: it’s shown only once.</p>
<p>Give each tool its own app. If a key leaks from one tool’s settings, you delete that app and only that tool stops texting.</p>
<p>Under the hood, all four tools make the same call: a POST to the <a href="https://smstunnel.io/api-reference/#send-sms">Send a Text endpoint</a> with the key in an <code>X-API-Key</code> header and one number per request:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">curl</span><span style="color:#79B8FF"> -X</span><span style="color:#9ECBFF"> POST</span><span style="color:#9ECBFF"> https://smstunnel.io/api/v1/sms/send</span><span style="color:#79B8FF"> \</span></span>
<span class="line"><span style="color:#79B8FF">  -H</span><span style="color:#9ECBFF"> "X-API-Key: sk_live_..."</span><span style="color:#79B8FF"> \</span></span>
<span class="line"><span style="color:#79B8FF">  -H</span><span style="color:#9ECBFF"> "Content-Type: application/json"</span><span style="color:#79B8FF"> \</span></span>
<span class="line"><span style="color:#79B8FF">  -d</span><span style="color:#9ECBFF"> '{"to": "+14155550123", "message": "web-01 is down"}'</span></span></code></pre>
<p>Numbers go in international format, with <code>+</code> and the country code. For two people, you make two requests: everyone gets their own text.</p>
<h2 id="uptime-kuma-the-built-in-webhook-notification">Uptime Kuma: the built-in Webhook notification</h2>
<p>Uptime Kuma needs no plugin. Open <strong>Settings → Notifications → Set Up Notification</strong>, choose the <strong>Webhook</strong> type, give it a name like “SMS to me”, and fill in:</p>
<ul>
<li><strong>Post URL</strong>: <code>https://smstunnel.io/api/v1/sms/send</code>, with <strong>HTTP Method</strong> POST if your version shows the field;</li>
<li><strong>Request Body</strong>: <strong>Custom Body</strong>, with your number in <code>to</code>;</li>
<li><strong>Additional Headers</strong>: turned on, with your key.</li>
</ul>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="text"><code><span class="line"><span>{"to": "+14155550123", "message": {{ msg | json }}}</span></span></code></pre>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="json"><code><span class="line"><span style="color:#E1E4E8">{</span><span style="color:#79B8FF">"Content-Type"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"application/json"</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">"X-API-Key"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"sk_live_..."</span><span style="color:#E1E4E8">}</span></span></code></pre>
<p>Keep <code>{{ msg | json }}</code> exactly as it is: it puts Uptime Kuma’s alert in the text, with the quotes JSON needs. Click <strong>Test</strong> and your phone texts <code>&#x3C;Friendly Name> Testing</code>; a wrong key shows a 401 “Invalid or expired API key”. Click <strong>Save</strong>, then tick the notification in each monitor’s <strong>Notifications</strong> list, or use <strong>Default enabled</strong> and <strong>Apply on all existing monitors</strong>. For several people, add one Webhook notification per number.</p>
<p>The texts look like this:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="text"><code><span class="line"><span>[My shop] [🔴 Down] timeout of 48000ms exceeded</span></span>
<span class="line"><span>[My shop] [✅ Up] 200 - OK</span></span></code></pre>
<p>A monitor that keeps flapping sends a text every time, so set <strong>Retries</strong> so that a short blip isn’t reported as down.</p>
<p>No key in Uptime Kuma at all? A webhook address works too: in your dashboard, open <strong>Apps → Webhooks → New webhook</strong> and pick <strong>Uptime Kuma</strong>, which sets the webhook’s text to <code>{{msg}}</code>. Then make the address the notification’s <strong>Post URL</strong>, with <strong>Request Body</strong> on <strong>Preset - application/json</strong> and no headers; one notification texts all the webhook’s numbers, up to 10. The <a href="https://smstunnel.io/integrations/uptime-kuma/#webhook-url">Uptime Kuma guide</a> has both ways.</p>
<h2 id="grafana-a-webhook-contact-point">Grafana: a webhook contact point</h2>
<p>On Grafana 12 or newer, open <strong>Alerts &#x26; IRM → Alerting → Notification configuration → Contact points</strong>, click <strong>+ Add contact point</strong> and choose the <strong>Webhook</strong> integration. Set the URL to <code>https://smstunnel.io/api/v1/sms/send</code>. Under <strong>Optional Webhook settings</strong>, choose <strong>HTTP Method</strong> POST and add an <strong>Extra Headers</strong> entry named <code>X-API-Key</code> with your key as the value. Then paste this into <strong>Custom Payload → Payload Template</strong>:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="text"><code><span class="line"><span>{{ coll.Dict</span></span>
<span class="line"><span>  "to" "+14155550123"</span></span>
<span class="line"><span>  "message" (printf "%s %s" (tmpl.Exec "default.title" .) (index .Alerts 0).Annotations.summary)</span></span>
<span class="line"><span>  | data.ToJSON }}</span></span></code></pre>
<p><code>coll.Dict … | data.ToJSON</code> writes the JSON for you, so quotes and colons in an alert can’t break it. Leave the Authorization and Basic Authentication fields empty, click <strong>Test</strong>, then <strong>Save contact point</strong>, and pick it in an alert rule or in <strong>Notification policies</strong>. A text reads like <code>[FIRING:1] DiskFull nas1  Disk 95% full: /volume1</code>, and again with <code>[RESOLVED]</code> when the alert clears.</p>
<p>Two things worth knowing. A firing alert texts again at every repeat interval, 4 hours by default, and once more when it resolves unless you tick <strong>Disable resolved message</strong>. And Grafana doesn’t mask Extra Headers: anyone who can edit the contact point can read the key, which is one more reason to give Grafana its own app.</p>
<p>Easier still, on any Grafana version and on Grafana Cloud: put a webhook address in the contact point’s URL and leave everything else as it is, with no key and no payload template (see the webhook section below). SMSTunnel reads Grafana’s own alert JSON and texts <code>[FIRING] DiskFull - Disk 95% full: /volume1</code> to the webhook’s numbers.</p>
<h2 id="zabbix-a-media-type-instead-of-a-gsm-modem">Zabbix: a media type instead of a GSM modem</h2>
<p>Zabbix’s built-in SMS needs a GSM modem on a serial port of the server. The <a href="https://smstunnel.io/integrations/zabbix/">SMSTunnel media type</a> needs only HTTPS, so it works the same on bare metal, in a VM, in Docker or in the cloud, for Zabbix 6.0 LTS, 7.0 LTS and later. Zabbix 6.0 has some of the menus elsewhere; they’re in brackets.</p>
<ol>
<li>Download <a href="https://github.com/narcisbodea/smstunnel-zabbix/releases/latest/download/media_smstunnel.yaml">media_smstunnel.yaml</a>, or <a href="https://github.com/narcisbodea/smstunnel-zabbix/releases/latest/download/media_smstunnel_6.0.yaml">media_smstunnel_6.0.yaml</a> for Zabbix 6.0, and import it in <strong>Alerts → Media types → Import</strong> (6.0: <strong>Administration → Media types → Import</strong>).</li>
<li>In <strong>Administration → Macros</strong> (6.0: <strong>Administration → General → Macros</strong>), add <code>{$SMSTUNNEL.API.KEY}</code> with your key and set its type to <strong>Secret text</strong>, so it’s hidden in the frontend and left out of exports.</li>
<li>Click <strong>Test</strong> in the SMSTunnel row and put your own number in <code>send_to</code>. The Test window doesn’t fill in macros, so paste the key into <code>api_key</code> for that test only.</li>
<li>Give each user their own number: in <strong>Users → Users</strong> (6.0: <strong>Administration → Users</strong>), open the user, then <strong>Media → Add</strong>, type <strong>SMSTunnel</strong>, with the number in <strong>Send to</strong> in international format.</li>
<li>In <strong>Alerts → Actions → Trigger actions</strong> (6.0: <strong>Configuration → Actions → Trigger actions</strong>), add an operation that sends to users or user groups, with <strong>Send only to: SMSTunnel</strong>.</li>
</ol>
<p><img src="https://smstunnel.io/zabbix-smstunnel-test.png" alt="The Test window of the SMSTunnel media type in Zabbix 7.0: &#x22;Media type test successful&#x22;, with the API key masked" loading="lazy" decoding="async" width="1440" height="900"></p>
<p>The media type brings message templates written to be read on a phone, and cuts texts at 320 characters:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="text"><code><span class="line"><span>PROBLEM: High CPU utilization (over 90% for 5m)</span></span>
<span class="line"><span>Host: web-01</span></span>
<span class="line"><span>Severity: High</span></span>
<span class="line"><span>Data: Current utilization: 97.3 %</span></span>
<span class="line"><span>Started: 2026.11.01 10:00:00</span></span>
<span class="line"><span>ID: 123456</span></span></code></pre>
<p>A busy moment or a short outage is retried for about two minutes (5 attempts, 30 seconds apart). Only the Zabbix server sends alerts, not a Zabbix proxy, so it’s the one that needs to reach smstunnel.io on port 443.</p>
<h2 id="github-actions-a-text-when-a-deployment-fails">GitHub Actions: a text when a deployment fails</h2>
<p>Save the key as the repository secret <code>SMSTUNNEL_API_KEY</code> (<strong>Settings → Secrets and variables → Actions</strong>). In a public repository, save your number as a secret too, such as <code>ALERT_PHONE</code>, because workflow files and logs are public. Then add a step that runs only when the job fails:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="yaml"><code><span class="line"><span style="color:#E1E4E8">- </span><span style="color:#85E89D">name</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">Text me if it failed</span></span>
<span class="line"><span style="color:#85E89D">  if</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">failure()</span></span>
<span class="line"><span style="color:#85E89D">  uses</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">narcisbodea/smstunnel-action@v1</span></span>
<span class="line"><span style="color:#85E89D">  with</span><span style="color:#E1E4E8">:</span></span>
<span class="line"><span style="color:#85E89D">    api-key</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">${{ secrets.SMSTUNNEL_API_KEY }}</span></span>
<span class="line"><span style="color:#85E89D">    to</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">${{ secrets.ALERT_PHONE }}</span></span>
<span class="line"><span style="color:#85E89D">    message</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"Deploy of ${{ github.repository }} failed at ${{ github.sha }}: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"</span></span></code></pre>
<p>Several numbers in <code>to</code>, separated by commas or new lines, get one text each (at most 30). GitHub gives no secrets to workflows that run for pull requests from forks or for Dependabot, so skip the step there, and don’t switch to <code>pull_request_target</code> to get around it. If a failed text shouldn’t fail your deployment, add <code>continue-on-error: true</code>. The <a href="https://smstunnel.io/integrations/github-actions/">GitHub Actions guide</a> has release and nightly-job examples.</p>
<h2 id="everything-else-a-webhook-address">Everything else: a webhook address</h2>
<p>Some tools can’t set a header or their own JSON body. For them, SMSTunnel has <a href="https://smstunnel.io/integrations/webhooks/">webhooks</a>: a secret address that texts the numbers you saved with it (1 to 10, one text each). The call sets only the words, never the number, so it needs no key, and a leaked address can’t text anyone new.</p>
<p>You make one in your <a href="https://dashboard.smstunnel.io/apps/webhooks?new=other">dashboard</a>: open <strong>Apps → Webhooks → New webhook</strong>, pick the tool that will call it (Grafana, Uptime Kuma, Healthchecks, Gatus, Alertmanager, Odoo Online, or Something else), add your numbers, click <strong>Create the webhook</strong> and copy the address. The dashboard then shows that tool’s steps with the address already in them, and <strong>Send a test</strong>. You can copy the address again anytime, and <strong>New address</strong> replaces it if it ever leaks.</p>
<p>A GET with <code>?message=</code> or a POST with a <code>message</code> field both work; texts are cut at 480 characters. Each webhook takes 20 calls an hour unless you raise it (up to 500), and the limit counts calls, not texts: each call texts every number of the webhook. Prometheus Alertmanager and Grafana alerts are read without any template; for Uptime Kuma, picking it sets the text to <code>{{msg}}</code>, so the monitor’s name doesn’t show twice. A crontab line, for example:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">0</span><span style="color:#79B8FF"> 3</span><span style="color:#79B8FF"> *</span><span style="color:#79B8FF"> *</span><span style="color:#79B8FF"> *</span><span style="color:#9ECBFF"> /opt/backup.sh</span><span style="color:#F97583"> ||</span><span style="color:#B392F0"> curl</span><span style="color:#79B8FF"> -s</span><span style="color:#79B8FF"> -X</span><span style="color:#9ECBFF"> POST</span><span style="color:#9ECBFF"> https://smstunnel.io/api/v1/webhook/</span><span style="color:#F97583">&#x3C;</span><span style="color:#9ECBFF">toke</span><span style="color:#E1E4E8">n</span><span style="color:#F97583">></span><span style="color:#79B8FF"> -H</span><span style="color:#9ECBFF"> 'Content-Type: application/json'</span><span style="color:#79B8FF"> -d</span><span style="color:#9ECBFF"> '{"message":"Nightly backup FAILED"}'</span></span></code></pre>
<p>Scripts can make webhooks with the API too; the <a href="https://smstunnel.io/integrations/webhooks/#api">webhooks guide</a> shows how. There are ready guides for <a href="https://smstunnel.io/integrations/alertmanager/#webhook-url">Prometheus Alertmanager</a>, <a href="https://smstunnel.io/integrations/healthchecks/#webhook-url">Healthchecks.io</a>, <a href="https://smstunnel.io/integrations/gatus/#webhook-url">Gatus</a> and <a href="https://smstunnel.io/integrations/odoo/#odoo-online">Odoo Online</a>, and, with a key, for <a href="https://smstunnel.io/integrations/proxmox/">Proxmox</a> and <a href="https://smstunnel.io/integrations/synology/">Synology</a>.</p>
<h2 id="limits-worth-knowing">Limits worth knowing</h2>
<ul>
<li><strong>30 send requests a minute.</strong> Beyond that SMSTunnel answers 429, and each tool logs it or retries. Group your alerts so a storm becomes one text.</li>
<li><strong>Length.</strong> A text can be up to 1600 characters. An emoji makes an SMS hold 70 characters instead of 160, and Uptime Kuma puts one in each alert, so a longer alert goes out as several joined parts.</li>
<li><strong>The phone must be on and online.</strong> If it isn’t, the text waits in your account and goes out when the phone is back, for up to 3 days. If an alert must never depend on one phone being on and online, an <a href="https://smstunnel.io/compare/">SMS API provider</a> fits that alert better.</li>
<li><strong>Plans.</strong> The <a href="https://smstunnel.io/pricing/">Free plan</a> covers 25 texts a day and 500 a month as written, plus up to 25 more a day ending with a short “free by smstunnel.io” line. Starter ($9 a month, before tax) allows 300 a day, Standard ($19) 2,000, and Professional ($39) has no daily cap.</li>
</ul>
<h2 id="common-questions">Common questions</h2>
<h3 id="do-i-need-a-gsm-modem-or-an-sms-provider">Do I need a GSM modem or an SMS provider?</h3>
<p>No. The texts leave your own phone, on the plan you already pay for, from a number people recognize.</p>
<h3 id="can-several-people-get-the-same-alert">Can several people get the same alert?</h3>
<p>Yes, one text each: one Uptime Kuma notification, Grafana integration or Zabbix media per number, up to 30 numbers in the GitHub Action’s <code>to</code>, or one webhook address with up to 10 numbers.</p>
<h3 id="where-should-the-key-live">Where should the key live?</h3>
<p>Where only admins can read it: Zabbix’s Secret text macro, a GitHub repository secret, the notification settings of Uptime Kuma or Grafana. Deleting the tool’s app in your dashboard cuts it off at once.</p>]]></content:encoded>
    </item>
    <item>
      <title>Let Claude, Cursor and VS Code send texts from your own phone</title>
      <link>https://smstunnel.io/blog/mcp-server-sms-claude-cursor/</link>
      <guid isPermaLink="true">https://smstunnel.io/blog/mcp-server-sms-claude-cursor/</guid>
      <pubDate>Tue, 03 Nov 2026 07:20:00 GMT</pubDate>
      <dc:creator>NARBOWEB SRL</dc:creator>
      <category>AI</category>
      <category>MCP</category>
      <description>Connect the SMSTunnel MCP server to Claude, Cursor or VS Code, and your AI assistant can text from your own phone and number, after you approve each text.</description>
      <content:encoded><![CDATA[<p><a href="https://www.youtube.com/watch?v=Rey_Kla6WkQ"><img src="https://smstunnel.io/video/setup-android.webp" alt="Set up SMSTunnel on your Android phone in 2 minutes" /></a></p><p>“Text Ana that I’m running ten minutes late.” Typed into Claude, that sentence can become a real text from your own number. The SMSTunnel MCP server gives Claude Desktop, Claude Code, Cursor and VS Code a few tools: the assistant hands the text to your SMSTunnel account, and the SMSTunnel app on your Android phone sends it from its own SIM. Ana sees a number she knows, and she can reply.</p>
<p>The server is published: the package <a href="https://www.npmjs.com/package/@narcisbodea/smstunnel-mcp"><code>@narcisbodea/smstunnel-mcp</code></a> on npm, the entry <code>io.github.narcisbodea/smstunnel-mcp</code> in the MCP Registry, and the <a href="https://github.com/narcisbodea/smstunnel-mcp">source code on GitHub</a> under the MIT license. The <a href="https://smstunnel.io/integrations/mcp/">MCP server page</a> has the same setup on one screen.</p>
<p>This guide sets it up in a few minutes and explains the rules built into it: you approve each text, every text goes to one person, and texts you receive are treated as data, never as instructions.</p>
<h2 id="what-the-assistant-can-do">What the assistant can do</h2>
<p>MCP, the Model Context Protocol, is an open standard that lets AI apps use outside tools. The SMSTunnel server adds six:</p>

































<table><thead><tr><th>Tool</th><th>What it does</th></tr></thead><tbody><tr><td><code>send_sms</code></td><td>Sends one text to one number. For several people, one text each.</td></tr><tr><td><code>send_sign_in_code</code></td><td>Sends a sign-in or verification code (a code of 4 to 10 digits).</td></tr><tr><td><code>get_message_status</code></td><td>Tells whether a text is pending, sent, delivered or failed.</td></tr><tr><td><code>list_phones</code></td><td>Lists your linked phones, and which are online.</td></tr><tr><td><code>get_usage</code></td><td>Shows your plan, what you sent today and what is left.</td></tr><tr><td><code>list_received_texts</code></td><td>Reads texts your phone received, only if you allow it.</td></tr></tbody></table>
<p>Every tool carries the MCP annotations that say whether it only reads or also changes something, so AI apps that support them can ask you before anything is sent.</p>
<h2 id="step-1-link-your-phone-and-create-a-key">Step 1: link your phone and create a key</h2>
<ol>
<li>Install the SMSTunnel app from Google Play on your Android phone and sign in. Sign in to your dashboard on your computer with the same account: the phone appears under <strong>Phones</strong>.</li>
<li>In the dashboard, open <strong>Apps → Add an app</strong> and copy its key. It is shown only once, and it can send texts and look up whether they went out.</li>
<li>Optional: to let the assistant read the texts you receive, turn on <strong>Can also read received texts</strong> when you add the app, and turn on syncing of received texts in the SMSTunnel app. Syncing is off by default, and only texts that arrive after you turn it on are synced.</li>
</ol>
<h2 id="step-2-add-the-server-to-your-ai-app">Step 2: add the server to your AI app</h2>
<p>You need <a href="https://nodejs.org">Node.js</a> 20 or newer. Your AI app starts the server with <code>npx</code>; you don’t run it yourself.</p>
<h3 id="claude-desktop-and-cursor">Claude Desktop and Cursor</h3>
<p>In Claude Desktop, open <strong>Settings → Developer → Edit Config</strong> (<code>claude_desktop_config.json</code>), add the server, then quit and restart Claude. Cursor takes the same block in <code>~/.cursor/mcp.json</code> (all projects) or <code>.cursor/mcp.json</code> (one project).</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="json"><code><span class="line"><span style="color:#E1E4E8">{</span></span>
<span class="line"><span style="color:#79B8FF">  "mcpServers"</span><span style="color:#E1E4E8">: {</span></span>
<span class="line"><span style="color:#79B8FF">    "smstunnel"</span><span style="color:#E1E4E8">: {</span></span>
<span class="line"><span style="color:#79B8FF">      "command"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"npx"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#79B8FF">      "args"</span><span style="color:#E1E4E8">: [</span><span style="color:#9ECBFF">"-y"</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">"@narcisbodea/smstunnel-mcp"</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#79B8FF">      "env"</span><span style="color:#E1E4E8">: {</span></span>
<span class="line"><span style="color:#79B8FF">        "SMSTUNNEL_API_KEY"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"sk_live_your_key"</span></span>
<span class="line"><span style="color:#E1E4E8">      }</span></span>
<span class="line"><span style="color:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<p>On Windows, if the app can’t start <code>npx</code>, use <code>"command": "cmd"</code> and <code>"args": ["/c", "npx", "-y", "@narcisbodea/smstunnel-mcp"]</code>.</p>
<h3 id="claude-code">Claude Code</h3>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="bash"><code><span class="line"><span style="color:#B392F0">claude</span><span style="color:#9ECBFF"> mcp</span><span style="color:#9ECBFF"> add</span><span style="color:#79B8FF"> --env</span><span style="color:#9ECBFF"> SMSTUNNEL_API_KEY=sk_live_your_key</span><span style="color:#79B8FF"> --transport</span><span style="color:#9ECBFF"> stdio</span><span style="color:#79B8FF"> --scope</span><span style="color:#9ECBFF"> user</span><span style="color:#9ECBFF"> smstunnel</span><span style="color:#79B8FF"> --</span><span style="color:#9ECBFF"> npx</span><span style="color:#79B8FF"> -y</span><span style="color:#9ECBFF"> @narcisbodea/smstunnel-mcp</span></span></code></pre>
<p>Run <code>/mcp</code> in Claude Code to check that <code>smstunnel</code> is connected.</p>
<h3 id="vs-code">VS Code</h3>
<p>In <code>.vscode/mcp.json</code>, VS Code asks for the key once and keeps it out of the file:</p>
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="json"><code><span class="line"><span style="color:#E1E4E8">{</span></span>
<span class="line"><span style="color:#79B8FF">  "inputs"</span><span style="color:#E1E4E8">: [</span></span>
<span class="line"><span style="color:#E1E4E8">    { </span><span style="color:#79B8FF">"type"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"promptString"</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">"id"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"smstunnel-api-key"</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">"description"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"SMSTunnel API key"</span><span style="color:#E1E4E8">, </span><span style="color:#79B8FF">"password"</span><span style="color:#E1E4E8">: </span><span style="color:#79B8FF">true</span><span style="color:#E1E4E8"> }</span></span>
<span class="line"><span style="color:#E1E4E8">  ],</span></span>
<span class="line"><span style="color:#79B8FF">  "servers"</span><span style="color:#E1E4E8">: {</span></span>
<span class="line"><span style="color:#79B8FF">    "smstunnel"</span><span style="color:#E1E4E8">: {</span></span>
<span class="line"><span style="color:#79B8FF">      "type"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"stdio"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#79B8FF">      "command"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"npx"</span><span style="color:#E1E4E8">,</span></span>
<span class="line"><span style="color:#79B8FF">      "args"</span><span style="color:#E1E4E8">: [</span><span style="color:#9ECBFF">"-y"</span><span style="color:#E1E4E8">, </span><span style="color:#9ECBFF">"@narcisbodea/smstunnel-mcp"</span><span style="color:#E1E4E8">],</span></span>
<span class="line"><span style="color:#79B8FF">      "env"</span><span style="color:#E1E4E8">: { </span><span style="color:#79B8FF">"SMSTUNNEL_API_KEY"</span><span style="color:#E1E4E8">: </span><span style="color:#9ECBFF">"${input:smstunnel-api-key}"</span><span style="color:#E1E4E8"> }</span></span>
<span class="line"><span style="color:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#E1E4E8">}</span></span></code></pre>
<h3 id="the-settings">The settings</h3>
<p>All settings go in the server’s <code>env</code>:</p>

























<table><thead><tr><th>Setting</th><th>What it does</th></tr></thead><tbody><tr><td><code>SMSTUNNEL_API_KEY</code></td><td>Required: the key from <strong>Apps → Add an app</strong>.</td></tr><tr><td><code>SMSTUNNEL_DEVICE_ID</code></td><td>Optional: the phone that sends when the assistant doesn’t name one. Its ID is under <strong>Phones</strong>.</td></tr><tr><td><code>SMSTUNNEL_DEFAULT_COUNTRY_CODE</code></td><td>Optional, like <code>+44</code>: completes numbers written without <code>+</code> or <code>00</code>.</td></tr><tr><td><code>SMSTUNNEL_CONFIRM_EACH_TEXT</code></td><td><code>true</code> by default: apps that support it, like Claude Code, ask before every text. Set <code>false</code> only for unattended runs, such as a script that texts you.</td></tr></tbody></table>
<h2 id="step-3-try-it">Step 3: try it</h2>
<p>Start with your own number, then try things like these:</p>
<ul>
<li>“Text +14155550123: the delivery is at the back door.”</li>
<li>“Did my last text go out?”</li>
<li>“Which of my phones are online?”</li>
<li>“How many texts do I have left today?”</li>
<li>“Did anyone reply to me since this morning?” (with received texts allowed)</li>
</ul>
<p>Before sending, the assistant shows you the number and the exact words and waits for your OK. Once the text is handed to your account, it gets a message ID and can tell you later whether the text went out.</p>
<h2 id="calendar-reminders-when-you-ask">Calendar reminders, when you ask</h2>
<p>Put SMSTunnel next to your calendar in the same AI app, and the assistant can read your appointments and text reminders from your own number. In Claude, open <strong>Customize → Connectors</strong> and connect <strong>Google Calendar</strong>, or <strong>Microsoft 365</strong> for an Outlook calendar on a work or school account. Then ask:</p>
<blockquote>
<p>“Look at my calendar for tomorrow and text each client with an appointment a reminder with the time. Show me the list first.”</p>
</blockquote>
<p>The assistant reads the events, finds each client’s number and shows you the whole list: each person, their number and the exact text. After your OK it sends one text per person, and it never resends a failed text on its own. Keep each client’s number in the event, with <code>+</code> and the country code, so the assistant can find it. Calendar events can also come from other people’s invitations, so read the list before you approve it.</p>
<p>The assistant acts when you ask; it doesn’t run on a schedule. For reminders that go out on their own, use the free <a href="https://smstunnel.io/integrations/google-calendar/">Google Calendar template</a> (Google Calendar stopped its own SMS notifications in January 2019) or the <a href="https://smstunnel.io/integrations/outlook-calendar/">Outlook Calendar recipes</a> for n8n, Power Automate, Zapier or Make.</p>
<h2 id="the-rules-built-into-the-server">The rules built into the server</h2>
<p>An assistant that can send texts needs clear limits. These are part of the server, not just advice:</p>
<ul>
<li><strong>You approve each text.</strong> The tools tell the assistant to show you the number and the words, or the whole list for several people, and to wait for your OK. With <code>SMSTUNNEL_CONFIRM_EACH_TEXT</code> left on, Claude Code asks before every single text, even in modes that otherwise run tools on their own.</li>
<li><strong>One recipient per text.</strong> There is no tool for several numbers: the assistant sends one text per person, about 30 a minute at most.</li>
<li><strong>No blind resends.</strong> A failed or unconfirmed text may still have gone out, so the assistant asks you instead of sending it again.</li>
<li><strong>Received texts are data.</strong> Anyone can text your phone, so the assistant treats their words as information, never as instructions. Turn reading on only if you need it.</li>
<li><strong>Your key stays in your AI app’s settings,</strong> never in a chat, a repository or a web page. Delete the app under <strong>Apps</strong> in your dashboard to cut it off at once.</li>
<li><strong>Only texts people expect:</strong> replies, reminders, alerts to yourself, sign-in codes.</li>
</ul>
<p>One more thing to know: texts sent through the MCP server are kept on your dashboard’s <strong>Messages</strong> page, like texts from your other apps. The server doesn’t encrypt them end to end; if you need that for texts from your own software, see <a href="https://smstunnel.io/blog/end-to-end-encrypted-sms/">how end-to-end encryption works</a>.</p>
<h2 id="what-about-chatgpt">What about ChatGPT?</h2>
<p>ChatGPT connects only to MCP servers on the internet, over HTTPS, so it can’t start this server on your computer. Use Claude Desktop, Claude Code, Cursor or VS Code. If you’d rather write your own code, the same things are one HTTPS call away in the <a href="https://smstunnel.io/api-reference/">API reference</a>.</p>
<h2 id="common-questions">Common questions</h2>
<h3 id="what-does-it-cost">What does it cost?</h3>
<p>The MCP server itself is free and open source. The texts count toward your SMSTunnel plan like texts from any of your apps. The Free plan includes 25 texts a day and 500 a month as you wrote them, plus up to 25 more a day that end with a short “free by smstunnel.io” line, and 50 sign-in codes a day. Paid plans start at $9 a month, before tax: see <a href="https://smstunnel.io/pricing/">Pricing</a>. Your mobile plan’s SMS charges or allowance apply, because your own SIM sends the texts.</p>
<h3 id="can-the-assistant-text-several-people">Can the assistant text several people?</h3>
<p>Yes, one person at a time: it shows you everyone it will text first, and after your OK it sends one text to each number. There is no call that sends one text to many numbers.</p>
<h3 id="which-phone-sends-the-text">Which phone sends the text?</h3>
<p>Any phone linked to your account can. Set <code>SMSTUNNEL_DEVICE_ID</code> to the ID of the phone you want by default, or ask the assistant to use a particular phone: it can list your phones with their IDs and use the one you name.</p>
<h3 id="can-i-stop-it">Can I stop it?</h3>
<p>Yes, at any time. Delete the app under <strong>Apps</strong> in your dashboard, and its key can’t send texts anymore. You can also remove the server from your AI app’s settings.</p>]]></content:encoded>
    </item>
  </channel>
</rss>
