TextMe Web App Reverse Engineering Report
Date: January 28, 2026
Target: web.textme-app.com / api.textme-app.com
Executive Summary
TextMe is a free calling/texting app with a web interface. The web app uses QR code-based authentication (similar to WhatsApp Web), making automated API access challenging without a valid mobile app session. The backend is Django-based with JWT authentication, running on AWS infrastructure.
Viability Assessment: ⚠️ CHALLENGING - Building an unofficial API wrapper is technically possible but requires:
- A valid mobile app account to authenticate
- Session token extraction from an authenticated session
- Maintaining WebSocket connections for real-time events
1. Infrastructure Overview
Domains Discovered
| Domain |
Purpose |
web.textme-app.com |
Web client (AngularJS SPA) |
api.textme-app.com |
REST API server |
webauth.textme-app.com |
QR code WebSocket auth |
pubsub.textme-app.com |
Real-time messaging WebSocket |
sentry.go-text.me |
Error tracking |
ng.textme-app.com |
Offer wall / monetization |
go-text.me |
Marketing / assets |
Tech Stack
- Frontend: AngularJS + Angular Material
- Backend: Django (evident from CSRF token naming)
- Auth: JWT tokens (
WWW-Authenticate: JWT realm="api")
- Real-time: NGINX PushStream + WebSockets
- VoIP: SIP.js for voice/video calls
- Hosting: AWS (ELB detected)
- Analytics: Google Analytics, Google Tag Manager
- Error Tracking: Sentry
2. Authentication System
Primary Auth Method: QR Code Login
The web app uses a WhatsApp Web-style QR code authentication:
- Web client generates a session UUID:
TMWEB-{uuid}
- Opens WebSocket:
wss://webauth.textme-app.com/ws/TMWEB-{uuid}/
- QR code encodes this session ID
- Mobile app scans QR and authenticates the session
- WebSocket receives auth token, web client stores JWT
Secondary Auth Methods (in mobile app)
- Email/password via
/api/auth-token/
- Social auth via
/api/auth-token-social/ (Google, Facebook)
- Token refresh via
/api/auth-token-refresh/
CSRF Protection
Cookie Name: csrftoken
Header Name: X-CSRFToken
JWT Token Flow
- After successful auth, JWT token issued
- Token sent in
Authorization: JWT <token> header
- Token refresh endpoint available for session extension
3. API Endpoints Discovered
Authentication
| Endpoint |
Method |
Description |
/api/auth-token/ |
POST |
Username/password login |
/api/auth-token-social/ |
POST |
Social media OAuth login |
/api/auth-token-refresh/ |
POST |
Refresh JWT token |
/api/auth-token-voip-jwt/ |
POST |
Get VoIP-specific JWT |
/api/register-device/ |
POST |
Register mobile device |
User Management
| Endpoint |
Method |
Description |
/api/user-info/ |
GET |
Get current user info |
/api/users/ |
GET |
User lookup/search |
/api/profile-picture/ |
POST |
Upload profile picture |
/api/public-profile/ |
GET |
Public profile data |
/api/settings/ |
GET/PUT |
User settings |
/api/change-password/ |
POST |
Change password |
/api/reset-password/ |
POST |
Reset password |
Phone Numbers
| Endpoint |
Method |
Description |
/api/phone-number/available-countries/ |
GET |
List available countries |
/api/phone-number/choose/ |
POST |
Select a phone number |
Messaging (Inferred)
| Endpoint |
Method |
Description |
/event/conversation/ |
WebSocket |
Real-time conversation events |
/api/group/ |
GET/POST |
Group chat management |
/api/attachment/ |
POST |
Send attachments |
/api/attachment/get-upload-url/ |
POST |
Get presigned upload URL |
Calls
| Endpoint |
Method |
Description |
/api/call/ |
POST |
Initiate/manage calls |
Store/Monetization
| Endpoint |
Method |
Description |
/api/store/stickers/ |
GET |
Available stickers |
/api/store/stickers/packages/ |
GET |
Sticker packs |
/api/inapp/transactions/ |
GET/POST |
In-app purchases |
/api/pricing/ |
GET |
Pricing info |
Misc
| Endpoint |
Method |
Description |
/api/web/signup/ |
POST |
Web signup |
/api/opengraph-resolver/ |
POST |
Resolve OpenGraph metadata |
4. Real-Time Communication
PushStream (NGINX Module)
Used for real-time messaging with multiple transport options:
- WebSocket:
wss://pubsub.textme-app.com/ws/textme
- EventSource:
/ev
- Long Polling:
/lp
- Stream:
/sub
WebSocket Authentication
wss://webauth.textme-app.com/ws/TMWEB-{session-uuid}/
VoIP (SIP.js)
Voice/video calls use SIP protocol over WebSocket:
- Codecs: OPUS, PCMA, PCMU
- Transport: TCP/WebSocket
5. Security Measures Observed
Anti-Bot Measures
- Google reCAPTCHA - Integrated but may not be active for all endpoints
- 403 Forbidden on suspicious requests - POST to auth endpoints blocked
- AWS WAF - Detected blocking patterns
Rate Limiting
- Not explicitly tested, but expected on auth endpoints
CORS
- Strict CORS policy, likely whitelisting only official domains
6. Mobile App Details
- Package:
com.textmeinc.textme3
- Facebook App ID:
288600224541553
- Available on: iOS, Android
- Company: TextMe, Inc.
7. Proof of Concept (Python)
"""
TextMe Unofficial API Client - Proof of Concept
WARNING: This is for educational purposes only.
Requires a valid JWT token obtained from an authenticated session.
"""
import requests
import websocket
import json
import uuid
from typing import Optional, Dict, Any
class TextMeAPI:
BASE_URL = "https://api.textme-app.com"
WS_AUTH_URL = "wss://webauth.textme-app.com/ws"
WS_PUBSUB_URL = "wss://pubsub.textme-app.com/ws/textme"
def __init__(self, jwt_token: Optional[str] = None):
self.jwt_token = jwt_token
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
"Accept": "application/json",
"Content-Type": "application/json",
})
if jwt_token:
self.session.headers["Authorization"] = f"JWT {jwt_token}"
def get_qr_session_id(self) -> str:
"""Generate a session ID for QR code authentication."""
return f"TMWEB-{uuid.uuid4()}"
def wait_for_qr_auth(self, session_id: str) -> Optional[str]:
"""
Connect to WebSocket and wait for QR code scan.
Returns JWT token on successful auth.
"""
ws_url = f"{self.WS_AUTH_URL}/{session_id}/"
def on_message(ws, message):
data = json.loads(message)
if "token" in data:
self.jwt_token = data["token"]
self.session.headers["Authorization"] = f"JWT {self.jwt_token}"
ws.close()
return data["token"]
ws = websocket.WebSocketApp(
ws_url,
on_message=on_message,
)
ws.run_forever()
return self.jwt_token
def get_user_info(self) -> Dict[str, Any]:
"""Get current user information."""
response = self.session.get(f"{self.BASE_URL}/api/user-info/")
response.raise_for_status()
return response.json()
def get_settings(self) -> Dict[str, Any]:
"""Get user settings."""
response = self.session.get(f"{self.BASE_URL}/api/settings/")
response.raise_for_status()
return response.json()
def get_available_countries(self) -> Dict[str, Any]:
"""Get available countries for phone numbers."""
response = self.session.get(f"{self.BASE_URL}/api/phone-number/available-countries/")
response.raise_for_status()
return response.json()
def refresh_token(self) -> str:
"""Refresh the JWT token."""
response = self.session.post(
f"{self.BASE_URL}/api/auth-token-refresh/",
json={"token": self.jwt_token}
)
response.raise_for_status()
data = response.json()
self.jwt_token = data["token"]
self.session.headers["Authorization"] = f"JWT {self.jwt_token}"
return self.jwt_token
def send_message(self, conversation_id: str, text: str) -> Dict[str, Any]:
"""
Send a text message.
NOTE: Exact endpoint structure needs verification from network capture.
"""
# This is speculative - actual endpoint needs verification
response = self.session.post(
f"{self.BASE_URL}/api/conversation/{conversation_id}/message/",
json={"text": text}
)
response.raise_for_status()
return response.json()
# Usage Example (requires manual token extraction)
if __name__ == "__main__":
# Method 1: QR Code Auth (needs mobile app scan)
api = TextMeAPI()
session_id = api.get_qr_session_id()
print(f"Scan this QR code with TextMe app:")
print(f"Session: {session_id}")
# token = api.wait_for_qr_auth(session_id)
# Method 2: Use extracted token
# token = "your_jwt_token_here"
# api = TextMeAPI(jwt_token=token)
# user = api.get_user_info()
# print(user)
8. Obstacles for Unofficial API
Critical Barriers
- QR Code Only Auth - No username/password on web, requires mobile app
- AWS WAF - Blocks suspicious POST requests
- CSRF Protection - Django CSRF tokens required
- Device Registration - May require registered device ID
Potential Workarounds
- Mobile App Interception - Proxy mobile traffic to capture tokens
- APK Reverse Engineering - Decompile Android app for API secrets
- Selenium/Puppeteer - Automate web login with real QR scan
- Browser Extension - Inject into authenticated session
9. Recommendations
For Building an Unofficial API
- Start with mobile app - Proxy traffic with mitmproxy/Charles
- Extract tokens - Get JWT from authenticated session
- Map conversation endpoints - Need network capture for message send/receive
- Handle real-time - Implement WebSocket client for pubsub
- Token management - Implement refresh token rotation
Legal Considerations
- Check TextMe Terms of Service before automated access
- Unofficial APIs may violate ToS
- Consider rate limiting to avoid account bans
10. Missing Information (Needs Further Research)
Appendix: Raw Findings
Sentry DSN
https://87d11b479de34e519af45bc5a47d4a9e@sentry.go-text.me/6
Facebook App ID
288600224541553
Server: nginx
Server: awselb/2.0
WWW-Authenticate: JWT realm="api"