com um clique
sms-gate-app
Send and receive SMS messages with your own Android device
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
Send and receive SMS messages with your own Android device
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional SOC
| name | sms-gate-app |
| description | Send and receive SMS messages with your own Android device |
| license | Apache-2.0 |
The Android SMS Gateway is a powerful application that transforms an Android device into a fully-featured SMS gateway server. It provides a RESTful API for sending and receiving SMS messages, supporting both text and binary data messages, with advanced features like multi-SIM support, MMS reception, webhook notifications, and robust authentication.
Key Capabilities:
This guide provides agents with everything needed to integrate SMS functionality into applications, scripts, or services.
Before you begin, ensure you have:
The API supports two authentication methods:
Simple username/password sent with each request.
Format:
Authorization: Basic <base64-encoded-username:password>
Example (cURL):
curl -X POST "https://api.sms-gate.app/3rdparty/v1/messages" \
-u "username:password" \
-H "Content-Type: application/json" \
-d '{"phoneNumbers": ["+1234567890"], "textMessage": {"text": "Hello"}}'
Limitations:
Modern token-based authentication with scopes and expiration.
Available in: Public Cloud and Private Server modes only (NOT available in Local mode)
Make a POST request to the token endpoint using Basic Auth:
Endpoint:
POST /3rdparty/v1/auth/token
Request:
curl -X POST "https://api.sms-gate.app/3rdparty/v1/auth/token" \
-u "username:password" \
-H "Content-Type: application/json" \
-d '{
"ttl": 3600,
"scopes": ["messages:send", "messages:read"]
}'
Response:
{
"id": "w8pxz0a4Fwa4xgzyCvSeC",
"token_type": "Bearer",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_at": "2025-11-22T08:45:00Z"
}
Include the token in the Authorization header:
Authorization: Bearer <your-jwt-token>
Scopes define permissions. Use the principle of least privilege:
| Scope | Description |
|---|---|
messages:send | Send SMS messages |
messages:read | Read individual message details |
messages:list | List and view messages |
messages:export | Export inbox messages |
devices:list | List registered devices |
devices:delete | Remove devices |
webhooks:list | List webhook configurations |
webhooks:write | Create/modify webhooks |
webhooks:delete | Remove webhooks |
settings:read | Read system settings |
settings:write | Modify system settings |
logs:read | Read system logs |
tokens:manage | Generate and revoke tokens |
all:any | Full access (use with caution) |
Revoke a token:
curl -X DELETE "https://api.sms-gate.app/3rdparty/v1/auth/token/{jti}" \
-H "Authorization: Bearer <your-token>"
Best Practices:
POST /3rdparty/v1/messages
The API supports two message types - text messages and data messages (binary). Only one type can be sent per request.
{
"textMessage": {
"text": "Your OTP is 1234"
},
"phoneNumbers": ["+1234567890"],
"deviceId": "optional-device-id",
"simNumber": 1,
"ttl": 3600,
"priority": 100
}
{
"dataMessage": {
"data": "SGVsbG8gRGF0YSBXb3JsZCE=",
"port": 53739
},
"phoneNumbers": ["+1234567890"],
"simNumber": 1,
"ttl": 3600,
"priority": 100
}
| Parameter | Type | Description | Default |
|---|---|---|---|
skipPhoneValidation | boolean | Disable E.164 phone number validation | false |
deviceActiveWithin | integer | Only target devices active within last N hours (0 = disabled) | 0 |
| Parameter | Type | Required | Description | Default |
|---|---|---|---|---|
textMessage.text | string | Conditional* | Text content (max 160 chars, auto-split if longer) | - |
dataMessage.data | string | Conditional* | Base64-encoded binary data (max 140 bytes) | - |
dataMessage.port | integer | Conditional* | Destination port (1-65535) | - |
phoneNumbers | array | Yes | Recipient phone numbers (E.164 format) | - |
deviceId | string | No | Target specific device ID | null |
simNumber | integer | No | SIM card slot number (1-3) | See multi-sim settings |
ttl | integer | No | Message TTL in seconds | never |
validUntil | string | No | Absolute expiration (RFC3339) | never |
priority | integer | No | Send priority (-128 to 127) | 0 |
isEncrypted | boolean | No | Message is encrypted | false |
*Exactly one of textMessage, dataMessage, or deprecated message field is required.
| Level | Range | Description |
|---|---|---|
| High | 100-127 | Bypasses rate limits, processed first |
| Normal | 0-99 | Standard processing (default) |
| Low | -128 to -1 | Lower priority, queued after normal |
Success (202 Accepted):
{
"id": "abc123def456",
"status": "queued",
"createdAt": "2025-06-22T10:30:00Z"
}
Error (4xx/5xx):
{
"error": "ValidationError",
"message": "Invalid phone number format",
"details": {}
}
curl -X POST "https://api.sms-gate.app/3rdparty/v1/messages?skipPhoneValidation=true" \
-u "username:password" \
-H "Content-Type: application/json" \
-d '{
"textMessage": {"text": "Your verification code is 1234"},
"phoneNumbers": ["+1234567890"],
"ttl": 600,
"priority": 100
}'
curl -X POST "https://api.sms-gate.app/3rdparty/v1/messages" \
-u "username:password" \
-H "Content-Type: application/json" \
-d '{
"dataMessage": {
"data": "SGVsbG8gRGF0YSBXb3JsZCE=",
"port": 53739
},
"phoneNumbers": ["+1234567890"],
"ttl": 3600
}'
Use Appropriate Priority
Set TTL Values
ttl or validUntil to prevent indefinite queuingHandle Long Messages
Batch Sending
Idempotency
Device Selection
deviceId to target specific devices in multi-device setupsdeviceActiveWithin to ensure device is onlineSIM Selection
simNumber to route messages through specific SIM cardssimNumber is not specifiedReceiving SMS messages is accomplished through webhook notifications. When an SMS arrives at the device, the app sends an HTTP POST request to your registered webhook endpoint with the message details.
Your webhook endpoint must:
127.0.0.1)Example webhook handler (Python Flask):
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhook', methods=['POST'])
def receive_webhook():
data = request.get_json()
print(f"Received event: {data['event']}")
if data['event'] == 'sms:received':
payload = data['payload']
print(f"SMS from {payload['phoneNumber']}: {payload['message']}")
# Process the message...
# Return 2xx to acknowledge receipt
return jsonify({'status': 'received'}), 200
return jsonify({'status': 'ignored'}), 200
if __name__ == '__main__':
app.run(ssl_context='adhoc') # Use proper SSL in production
Use the webhook registration API to add your endpoint:
Endpoint:
POST /3rdparty/v1/webhooks
Request (Cloud mode example):
curl -X POST -u "username:password" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhook",
"event": "sms:received"
}' \
https://api.sms-gate.app/3rdparty/v1/webhooks
For a specific device:
curl -X POST -u "username:password" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhook",
"event": "sms:received",
"deviceId": "deviceId"
}' \
https://api.sms-gate.app/3rdparty/v1/webhooks
Note: Each webhook is registered for a single event. Register separate webhooks for multiple events.
Via API:
curl -X GET -u "username:password" \
https://api.sms-gate.app/3rdparty/v1/webhooks
Via App:
Trigger the event:
sms:received: Send an SMS to the devicesms:data-received: Send a data SMS to port 53739mms:received: Send an MMS messagesms:sent/delivered/failed: Send an SMS from the appsystem:ping: Enable ping in Settings > Pingapp:started: Start the appTriggered when a text SMS is received.
Payload:
{
"deviceId": "ffffffffceb0b1db0000018e937c815b",
"event": "sms:received",
"id": "Ey6ECgOkVVFjz3CL48B8C",
"payload": {
"messageId": "abc123",
"message": "Android is always a sweet treat!",
"phoneNumber": "6505551212",
"simNumber": 1,
"receivedAt": "2024-06-22T15:46:11.000+07:00"
},
"webhookId": "LreFUt-Z3sSq0JufY9uWB"
}
Triggered when a data SMS (binary) is received.
Payload:
{
"deviceId": "ffffffffceb0b1db0000018e937c815b",
"event": "sms:data-received",
"id": "Ey6ECgOkVVFjz3CL48B8C",
"payload": {
"messageId": "abc123",
"data": "SGVsbG8gRGF0YSBXb3JsZCE=",
"phoneNumber": "6505551212",
"simNumber": 1,
"receivedAt": "2024-06-22T15:46:11.000+07:00"
},
"webhookId": "LreFUt-Z3sSq0JufY9uWB"
}
Triggered when an MMS message is received (receive-only, no attachment content).
Payload:
{
"deviceId": "ffffffffceb0b1db0000018e937c815b",
"event": "mms:received",
"id": "Ey6ECgOkVVFjz3CL48B8C",
"payload": {
"messageId": "mms_12345abcde",
"phoneNumber": "+1234567890",
"simNumber": 1,
"transactionId": "T1234567890ABC",
"subject": "Photo attachment",
"size": 125684,
"contentClass": "IMAGE_BASIC",
"receivedAt": "2025-08-23T05:15:30.000+07:00"
},
"webhookId": "LreFUt-Z3sSq0JufY9uWB"
}
Triggered when an outgoing message is successfully sent from the device.
Payload:
{
"deviceId": "ffffffffceb0b1db0000018e937c815b",
"event": "sms:sent",
"id": "Ey6ECgOkVVFjz3CL48B8C",
"payload": {
"messageId": "abc123",
"phoneNumber": "+1234567890",
"simNumber": 1,
"partsCount": 1,
"sentAt": "2024-06-22T15:46:11.000+07:00"
},
"webhookId": "LreFUt-Z3sSq0JufY9uWB"
}
Triggered when delivery confirmation is received from the carrier. Note: For multipart messages, this fires once for each part.
Payload:
{
"deviceId": "ffffffffceb0b1db0000018e937c815b",
"event": "sms:delivered",
"id": "Ey6ECgOkVVFjz3CL48B8C",
"payload": {
"messageId": "abc123",
"phoneNumber": "+1234567890",
"simNumber": 1,
"deliveredAt": "2024-06-22T15:46:11.000+07:00"
},
"webhookId": "LreFUt-Z3sSq0JufY9uWB"
}
Triggered when message sending or delivery fails.
Payload:
{
"deviceId": "ffffffffceb0b1db0000018e937c815b",
"event": "sms:failed",
"id": "Ey6ECgOkVVFjz3CL48B8C",
"payload": {
"messageId": "abc123",
"phoneNumber": "+1234567890",
"simNumber": 1,
"reason": "RESULT_ERROR_LIMIT_EXCEEDED",
"failedAt": "2024-06-22T15:46:11.000+07:00"
},
"webhookId": "LreFUt-Z3sSq0JufY9uWB"
}
Triggered periodically when ping feature is enabled (health check).
Payload:
{
"deviceId": "ffffffffceb0b1db0000018e937c815b",
"event": "system:ping",
"id": "Ey6ECgOkVVFjz3CL48B8C",
"payload": {
"health": {
"status": "healthy",
"timestamp": "2024-06-22T15:46:11.000+07:00"
}
},
"webhookId": "LreFUt-Z3sSq0JufY9uWB"
}
Triggered when the application is started (launched via home screen or by the system in response to a boot or other event). Provides the current SIM card configuration.
Payload:
{
"deviceId": "ffffffffceb0b1db0000018e937c815b",
"event": "app:started",
"id": "Ey6ECgOkVVFjz3CL48B8C",
"payload": {
"simCards": [
{
"slotIndex": 0,
"simNumber": 1,
"phoneNumber": "+79990001234",
"carrierName": "MTS",
"iccid": "897010112233445"
}
]
},
"webhookId": "LreFUt-Z3sSq0JufY9uWB"
}
If your server doesn't respond with 2xx within 30 seconds, the app retries with exponential backoff:
Configure custom retry count in Settings > Webhooks on the device.
All webhook requests are signed with HMAC-SHA256 for verification.
Headers included:
X-Signature - Hexadecimal HMAC signatureX-Timestamp - Unix timestamp (seconds) used in signatureSigning key: Randomly generated on first request, configurable in Settings > Webhooks > Signing Key
X-Timestamp valueX-Signature headerExample (Python):
import hmac
import hashlib
def verify_signature(secret_key: str, payload: str, timestamp: str, signature: str) -> bool:
"""Verify webhook signature"""
message = (payload + timestamp).encode()
expected_signature = hmac.new(
secret_key.encode(),
message,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected_signature, signature)
# Flask example
@app.route('/webhook', methods=['POST'])
def webhook():
secret_key = os.environ['WEBHOOK_SIGNING_KEY']
payload = request.get_data(as_text=True)
timestamp = request.headers.get('X-Timestamp')
signature = request.headers.get('X-Signature')
if not verify_signature(secret_key, payload, timestamp, signature):
return 'Invalid signature', 401
# Process webhook...
return 'OK', 200
Security Best Practices:
For messages longer than standard limits (160 chars GSM-7 or 70 chars Unicode), the app auto-splits into multiple parts. This affects webhook delivery:
| Event | Behavior |
|---|---|
sms:received | Triggered once after all parts are received and assembled |
sms:sent | Triggered once when all parts are sent; partsCount indicates number of parts |
sms:delivered | Triggered once for each individual part |
sms:failed | Triggered once if any part fails |
Deduplication tip: Use messageId to group related delivery reports. Consider message fully delivered when you've received confirmations for all expected parts.
Data SMS enables transmission of binary data payloads (up to 140 bytes) via traditional SMS, without requiring mobile data connectivity.
Use Cases:
Receiving Data SMS:
Webhook event: sms:data-received with data field containing base64-encoded binary payload.
Limitations:
For devices with multiple SIM cards, the app provides:
Specify simNumber in your API request (1-based index):
curl -X POST "https://api.sms-gate.app/3rdparty/v1/messages" \
-u "username:password" \
-H "Content-Type: application/json" \
-d '{
"textMessage": {"text": "Hello from SIM 2!"},
"phoneNumbers": ["+1234567890"],
"simNumber": 2
}'
When simNumber is not specified, configure rotation in the app (Settings > Messages > SIM Selection):
All webhook events include simNumber field indicating which SIM was used (null if not applicable).
The app can receive MMS messages and notify via webhooks. Sending MMS is not supported.
Prerequisites:
RECEIVE_MMS permission grantedRECEIVE_SMS permissionWebhook Event: mms:received
Payload:
{
"event": "mms:received",
"payload": {
"messageId": "mms_12345abcde",
"phoneNumber": "+1234567890",
"simNumber": 1,
"transactionId": "T1234567890ABC",
"subject": "Photo attachment",
"size": 125684,
"contentClass": "IMAGE_BASIC",
"receivedAt": "2025-08-23T05:15:30.000+07:00"
}
}
Note: Webhooks provide metadata only, not actual attachment content. Attachments are stored locally on the device.
| Status | Meaning | Action |
|---|---|---|
| 200/201 | Success | - |
| 202 | Accepted | Message queued for delivery |
| 400 | Bad Request | Check request payload, validate parameters |
| 401 | Unauthorized | Invalid credentials or expired token |
| 403 | Forbidden | Insufficient scopes (JWT) or wrong password |
| 404 | Not Found | Endpoint or resource doesn't exist |
| 409 | Conflict | Duplicate message ID or resource conflict |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Retry with exponential backoff |
| 501 | Not Implemented | Feature not available in current mode (e.g., JWT in Local mode) |
| 503 | Service Unavailable | Server temporarily unavailable, retry (or queue limits exceeded - see below) |
Validation Error:
{
"error": "ValidationError",
"message": "Invalid phone number format",
"details": {
"phoneNumbers": ["Must be valid E.164 format"]
}
}
Rate Limit Exceeded:
{
"error": "RateLimitExceeded",
"message": "Message rate limit exceeded",
"retryAfter": 60
}
Queue Limit Exceeded (503 Service Unavailable): When the server's message queue limits are exceeded, the API returns a 503 error:
{
"error": "QueueLimitExceeded",
"message": "queue limits exceeded: <reason>"
}
Common reasons:
too many pending messages: X / Y - Device queue has too many pending messagestoo old pending message: <timestamp> - Oldest pending message exceeds the configured age limittoo many failed messages - All recent messages have failedResolution:
Authentication Errors:
Device Not Available:
{
"error": "NoActiveDevice",
"message": "No active device found"
}
Implement Retry Logic
Retry-After header if presentToken Refresh (JWT)
Validate Inputs
Log Errors
Graceful Degradation
Use JWT Authentication
Secure Credential Storage
Enforce HTTPS
Scope Limitation
Verify Signatures
X-Signature headerSecure Endpoint
Rate Limiting
Input Validation
Encryption
isEncrypted: true for sensitive messagesData Retention
Logging
Checklist:
Checklist:
sms:received, etc.)Common causes:
Fix: Use raw payload before JSON parsing and ensure constant-time comparison.
Causes:
Solutions:
401 Unauthorized:
403 Forbidden:
Checklist:
SEND_SMS permission grantedChecklist:
simNumber is valid (1-3 based on number of SIMs)simNumberCommon issues:
127.0.0.1 allowed)Solutions:
adb reverse tcp:9876 tcp:8080Checklist:
deviceActiveWithin parameter)skipPhoneValidation=true)| Mode | Base URL |
|---|---|
| Cloud | https://api.sms-gate.app |
| Private | Your custom domain |
| Local | http://<device-local-ip>:8080 |
| Feature | JWT | Basic Auth |
|---|---|---|
| Security | High | Medium |
| Access Control | Scopes | All-or-nothing |
| Token Management | Yes (TTL, revocation) | No |
| Recommended | ✅ Yes | ⚠️ Legacy only |
# Send SMS (Basic Auth)
curl -X POST "https://api.sms-gate.app/3rdparty/v1/messages" \
-u "username:password" \
-H "Content-Type: application/json" \
-d '{"phoneNumbers": ["+1234567890"], "textMessage": {"text": "Hello"}}'
# Generate JWT token
curl -X POST "https://api.sms-gate.app/3rdparty/v1/auth/token" \
-u "username:password" \
-H "Content-Type: application/json" \
-d '{"ttl": 3600, "scopes": ["messages:send"]}'
# Register webhook
curl -X POST -u "username:password" \
-H "Content-Type: application/json" \
-d '{"url": "https://your-server.com/webhook", "event": "sms:received"}' \
https://api.sms-gate.app/3rdparty/v1/webhooks
# List webhooks
curl -X GET -u "username:password" \
https://api.sms-gate.app/3rdparty/v1/webhooks
# List devices
curl -X GET -u "username:password" \
https://api.sms-gate.app/3rdparty/v1/devices