XSender lets you turn an Android phone running the XSender Android Gateway app into an SMS-sending device. This page documents the HTTP API the Android app uses, so you can register, link, relink, or remotely manage devices from another application (your own mobile app, a Flutter wrapper, an MDM tool, a fleet-management dashboard, etc.).
Gateway → Android SMS → Devices → Add Device.POST /api/gateway/sms/android/register-session with that token. XSender exchanges it for a long-lived session token and creates a Gateway + WhatsappDevice/AndroidSession row.GET /api/logs/sms/pending for queued messages and POSTs delivery results to /api/logs/sms/update-statuses./api/gateway/sms/android/sim/* CRUD endpoints.All endpoints (except register-session) require the long-lived session token in a Bearer header:
Authorization: Bearer eyJ0eXAiOiJKV1Qi...
The token is issued by register-session and bound to one device. Calling logout revokes it.
POST /api/gateway/sms/android/register-sessionThis is the one and only un-authenticated endpoint. It takes a one-time pairing token (the QR-code value shown in the XSender panel) and returns a long-lived session token.
{
"pairing_token": "ATKN-9f2d6c1b-...",
"device_name": "Office Phone — Pixel 7",
"device_uuid": "a1b2c3d4-e5f6-...",
"android_version": "14",
"app_version": "1.4.0"
}
{
"success": true,
"data": {
"session_token": "eyJ0eXAiOiJKV1Qi...",
"gateway_uid": "0f2c1b8d-9e6a-4f0b-bcde-1234567890ab",
"user": { "id": 17, "name": "Acme Marketing" },
"sims": [
{ "uid": "sim-1", "slot": 0, "label": "Main", "active": true },
{ "uid": "sim-2", "slot": 1, "label": "OTP", "active": false }
]
}
}
Re-linking: if the device's device_uuid matches a previously registered device, the existing session is reactivated and the same gateway_uid is returned. You do not get duplicate gateways — perfect for "factory reset → re-pair" flows.
POST /api/gateway/sms/android/logoutHeader: Authorization: Bearer {session_token}. Marks the gateway offline and revokes the token. The device row stays in the database — re-running register-session with the same device_uuid brings it back to life.
/api/gateway/sms/android/sim| Method | Path | Purpose |
|---|---|---|
| POST | /sim | Create a SIM row (slot, label, country code). |
| PUT | /sim/{uid} | Update a SIM (label, country code, active flag). |
| DELETE | /sim/{uid} | Remove a SIM. Active campaigns assigned to it stop. |
| POST | /sim/update-status | Report a SIM's quota / signal / IMSI on a heartbeat. |
{
"sim_uid": "sim-1",
"active": true,
"signal_strength": -78,
"messages_sent_24h": 412,
"imsi": "310260000000001"
}
GET /api/logs/sms/pending{
"success": true,
"data": [
{
"uid": "msg-9c2e",
"number": "+15555550123",
"body": "Your OTP is 482915",
"sim_uid": "sim-2",
"priority": "high"
}
]
}
POST /api/logs/sms/update-statuses{
"statuses": [
{ "uid": "msg-9c2e", "status": "sent", "delivered_at": "2026-05-23 12:01:14" },
{ "uid": "msg-aa10", "status": "failed", "error": "RADIO_NOT_AVAILABLE" }
]
}
Because every endpoint is plain HTTP + JSON with token auth, you can ship your own native or cross-platform client — Flutter, React Native, Kotlin, iOS, even a desktop relay.
Minimum behaviour for a viable client:
register-session; persist the returned session_token in secure storage.GET /api/logs/sms/pending every 5–15 s (configurable in the device profile)./api/logs/sms/update-statuses./sim/update-status with current signal, quota, and active flag — the panel uses this to show the device as online.logout or the admin panel.pending never returns more than the device's remaining quota.