Textpipe - SMS API Gateway
An Android app that turns your phone into an SMS API gateway with a web interface.
Features
- REST API - Send and receive SMS via HTTP endpoints
- Dual SIM Support - Each SIM gets its own API key
- Web UI - Google Messages-like interface accessible from any browser
- Local Database - All messages stored locally with delivery tracking
- Background Service - Runs as a foreground service with persistent notification
- Auto-start - Optionally start on device boot
- Root Port Redirect - Use ports 80/443 with root access via iptables
Requirements
- Android 7.0+ (API 24+)
- SMS permissions
- Phone state permissions (for dual SIM)
- (Optional) Root access for privileged ports
Building
Prerequisites
- Android Studio or command line with Android SDK
- JDK 11+
- Node.js 18+ (for web UI)
Build Steps
-
Clone the repository
git clone <repo-url> cd textpipe -
Build the Web UI
cd webui npm install npm run build cd ..This builds the Vue frontend to
app/src/main/assets/webui/ -
Build the Android App
./gradlew assembleDebugThe APK will be at
app/build/outputs/apk/debug/app-debug.apk -
Install on device
adb install app/build/outputs/apk/debug/app-debug.apk
Release Build
./gradlew assembleRelease
Note: You'll need to configure signing in app/build.gradle.kts for release builds.
Usage
Initial Setup
- Install and open the app
- Grant all requested permissions (SMS, Phone, Notifications)
- Note the API key(s) shown for each SIM card
- Configure the port (default: 8080)
- Tap "Start Server"
- (Optional) Disable battery optimization for reliable background operation
API Endpoints
Base URL: http://<phone-ip>:<port>
Status (No auth required)
GET /api/status
Returns server status and SIM information.
Send SMS
POST /api/sms/send
Header: X-API-Key: <your-api-key>
Content-Type: application/json
{
"to": "+1234567890",
"text": "Hello from Textpipe!"
}
Response:
{
"id": 1,
"status": "pending",
"simSlot": 0
}
List Messages
GET /api/sms/messages
Header: X-API-Key: <your-api-key>
Optional query parameter: ?type=sent or ?type=received
Response:
{
"messages": [
{
"id": 1,
"type": "sent",
"address": "+1234567890",
"text": "Hello!",
"simSlot": 0,
"status": "delivered",
"timestamp": 1707148800000,
"deliveryTimestamp": 1707148805000
}
],
"total": 1
}
Get Message Status
GET /api/sms/status/{id}
Header: X-API-Key: <your-api-key>
Authentication
Pass your API key in one of these ways:
- Header:
Authorization: Bearer <key> - Header:
X-API-Key: <key> - Query param:
?api_key=<key>
Each SIM card has its own unique API key. The API key determines which SIM sends the message.
Web Interface
Open http://<phone-ip>:<port>/ in any browser to access the web UI.
- Enter your API key to login
- View conversations grouped by contact
- Send new messages using the + button
- Click on a conversation to view message thread
cURL Examples
Send SMS:
curl -X POST http://10.0.1.12:8080/api/sms/send \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key-here" \
-d '{"to": "+1234567890", "text": "Hello!"}'
Check status:
curl http://10.0.1.12:8080/api/status
List messages:
curl -H "X-API-Key: your-api-key-here" \
http://10.0.1.12:8080/api/sms/messages
Get specific message:
curl -H "X-API-Key: your-api-key-here" \
http://10.0.1.12:8080/api/sms/status/1
Configuration
Port Configuration
- Default port: 8080
- Can be changed in the app UI
- For ports below 1024 (like 80 or 443), root access is required
- With root, the app uses iptables to redirect traffic from the privileged port to the actual server port
Auto-start
Enable "Auto-start on boot" in Settings to automatically start the server when the device boots.
Battery Optimization
For reliable background operation, disable battery optimization for Textpipe:
- Go to Settings in the app
- Tap "Disable" next to Battery Optimization
- Confirm in the system dialog
Project Structure
textpipe/
├── app/ # Android app
│ └── src/main/
│ ├── java/sh/sar/textpipe/
│ │ ├── MainActivity.kt
│ │ ├── TextpipeApplication.kt
│ │ ├── data/
│ │ │ ├── db/ # Room database
│ │ │ ├── model/ # API models
│ │ │ └── repository/ # Data repository
│ │ ├── server/ # Ktor HTTP server
│ │ │ ├── TextpipeServer.kt
│ │ │ ├── auth/ # API key auth
│ │ │ └── routes/ # API endpoints
│ │ ├── service/ # Android services
│ │ │ ├── TextpipeService.kt
│ │ │ ├── SmsSender.kt
│ │ │ ├── SmsReceiver.kt
│ │ │ └── BootReceiver.kt
│ │ ├── sim/ # SIM management
│ │ ├── root/ # Root utilities
│ │ └── ui/ # Compose UI
│ ├── assets/webui/ # Built web UI
│ └── AndroidManifest.xml
├── webui/ # Vue.js web interface
│ ├── src/
│ │ ├── App.vue
│ │ ├── api.js
│ │ ├── style.css
│ │ └── components/
│ ├── package.json
│ └── vite.config.js
├── gradle/
│ └── libs.versions.toml # Dependency versions
└── build.gradle.kts
Tech Stack
Android App
- Kotlin
- Jetpack Compose (UI)
- Ktor CIO (HTTP server)
- Room (Database)
- Kotlin Coroutines
- kotlinx.serialization (JSON)
Web UI
- Vue 3
- Vite
- Vanilla CSS
Message Status Flow
pending → sent → delivered
↘ failed
- pending - Message queued, sending in progress
- sent - Message sent successfully
- delivered - Delivery confirmation received
- failed - Send failed
Troubleshooting
"Address already in use" error
The port is still bound from a previous instance. Either:
- Wait a few seconds and try again
- Change to a different port
- Force stop the app and restart
Server doesn't start
- Check that no other app is using the port
- Verify all permissions are granted
- Check logcat for errors:
adb logcat -s TextpipeServer TextpipeService
Messages not sending
- Verify SMS permission is granted
- Check you have cellular signal
- Ensure the API key matches an active SIM
Web UI not loading
- Verify the server is running (check app status)
- Check you're using the correct IP and port
- Try accessing
/api/statusdirectly
License
MIT