Ever wondered how OTP systems work? In this blog, we’ll build a simple SMS OTP Sender using your Android phone, Termux, and a little bit of Python magic. It’s a fun way to learn about messaging automation, APIs, and port forwarding — especially if you're a beginner in backend or ethical hacking!
🛠️ Tools & Technologies Used
- Termux (Android) – Linux terminal emulator for Android.
- Termux: API – Provides access to Android’s native APIs like SMS.
- Python – To build a simple backend script.
- Flask – Lightweight Python web framework.
- Cloudflare Tunnel / Ngrok – To expose the local server to the internet.
📦 Step 1: Setup Termux on Android
Install Termux from F-Droid (not Play Store):
pkg update && pkg upgrade
pkg install python
pkg install termux-api
pip install flask
Also install Termux API app from F-Droid (important).
Step 2: Write the SMS Sender in Python
Create a file called sms_sender.py:
import json
import os
from http.server import BaseHTTPRequestHandler, HTTPServer
class RequestHandler(BaseHTTPRequestHandler):
def do_POST(self):
if self.path == "/send-sms":
content_length = int(self.headers["Content-Length"])
post_data = self.rfile.read(content_length)
data = json.loads(post_data)
phone = data.get("phone")
otp = data.get("otp")
if phone and otp:
command = f'termux-sms-send -n {phone} "Your OTP is {otp}"'
print(f"Executing: {command}") # Debugging statement
# Use os.system to execute the command
result = os.system(command)
# Check the result code
if result == 0:
print("✅ SMS sent successfully!")
self.send_response(200)
self.end_headers()
self.wfile.write(json.dumps({"message": "OTP Sent"}).encode())
else:
print("❌ Failed to send SMS! Error code:", result)
self.send_response(500)
self.end_headers()
self.wfile.write(json.dumps({"error": "Failed to send SMS"}).encode())
else:
self.send_response(400)
self.end_headers()
self.wfile.write(json.dumps({"error": "Invalid data"}).encode())
server_address = ("", 8080) # Running server on port 8080
httpd = HTTPServer(server_address, RequestHandler)
print("📡 Termux SMS Server running on port 8080...")
httpd.serve_forever()
🌐 Step 3: Port Forward with Cloudflare Tunnel
When you're building a local project, your services usually run on your machine and are only accessible from your own device. For example:
- Your frontend (React, etc.) runs on localhost:3000
- Your backend server runs on localhost:8080
- Your database runs on localhost:5432
But here’s the problem:
These localhost ports are not accessible from outside your machine.
So, how do you access your local app from another device? Or share it with a team member or webhook service?
You have two options:
Buy a domain + server, deploy your services, and make them public.
Or — use port forwarding with tunneling services like Cloudflare Tunnel or ngrok.
🔁 What is Port Forwarding?
Port forwarding is a method to expose a specific port (running locally) to the internet, by tunneling it through a public URL.
👉Example: Cloudflare Tunnel for Port Forwarding
Let’s say we’re working on a full-stack app that runs locally like this:
3000 → Frontend (React app)
8080 → Backend Server (API)
5432 → PostgreSQL Database
Here's a visual representation of our local setup:
As shown in the image:
All services are running locally.
We use a Cloudflare Tunnel to expose these local ports to the outside world.
This tunnel creates a public URL that anyone can access — just like a real deployed app.
To forward your local Flask server (running on port 8080), use:
cloudflared tunnel --url http://localhost:8080
🧪 Step 4: Test the SMS API
Here once the Cloudflare Tunnel was set up and pointing to to my Termux Flask server, we need a way to trigger rigger the OTP sending from my backend. So I created an API route that would generate an OTP, save it in the database for short duration, and send a request to my Termux SMS server to deliver the OTP to the user’s phone.
Here's how I tested it:
📡 Make a POST request to the Termux server
I exposed the Termux Flask server (running on my phone) using Cloudflare Tunnel. Then, I hit this public URL with a simple POST request:
POST https://yourname.trycloudflare.com/send-sms
Content-Type: application/json
{
"number": "9876543210",
"message": "Your OTP is 6789"
}
You can do this using Postman, or directly from terminal with curl:
curl -X POST https://yourname.trycloudflare.com/send-sms \
-H "Content-Type: application/json" \
-d '{"number": "9876543210", "message": "Your OTP is 6789"}'
And boom 💥 — the message is sent directly from my Android phone using Termux’s native termux-sms-send command.
This worked great for me during testing — no need for third-party SMS providers or paid APIs. I used this method to automate OTP delivery in my full-stack app.
📌 TL;DR
In this blog, I built a DIY SMS OTP sender using just:
- An Android phone running Termux
- A Python + Flask server to send SMS via termux-sms-send
- A Cloudflare Tunnel to expose the local API publicly
I also connected this setup with a backend (Node.js + MongoDB) that:
- Generates an OTP
- Stores it temporarily
- Verifies it securely when submitted
This project avoids third-party SMS services, is perfect for local development/testing, and helps you understand port forwarding, automation, and full-stack OTP systems using open tools.
Top comments (0)