Save 44% with yearly billing, 5 months free.View Plans
textbee Logotextbee.dev
Bring Your Own SIM: Android SMS Gateways in Production and What Breaks
Operations
Dual sim
Reliability
Small business
SMS
Android

Bring Your Own SIM: Android SMS Gateways in Production and What Breaks

Dual SIM quirks, batch pacing, async delivery states, battery optimization, and inbound gaps: an operations guide for teams sending SMS from their own phones.

TT

textbee team

11 min read
Share

TL;DR

  • Running SMS through your own phone works, but you now operate hardware. Budget for five things: a dedicated device on a charger, one unambiguous SIM, an async delivery model, battery optimization exemptions, and a reconciliation sweep.
  • The highest-return change is a spare Android on permanent power with battery optimization off. Most "the API is broken" reports are a phone put to sleep.
  • HTTP 200 does not mean delivered. A send is queued: pending, then dispatched, sent, delivered.
  • Throughput is bounded by the phone and the carrier: hundreds to low thousands of messages per day per device, not hundreds per second.
  • Go hybrid when it fits. Your own SIM for transactional sends, a cloud provider for marketing bursts, short codes, or an SLA.

"Use your phone as an SMS gateway" sounds simple: HTTP in, SMS out. In production the friction is never the HTTP call. It is device state, carrier behaviour, and a mental model borrowed from cloud SMS APIs that does not fit a phone. You send from your own number with no per-message fee, and in exchange uptime is your phone's uptime. For the architecture overview, read what an Android SMS gateway is. This post is the operations half.

What breaks #1: dual SIM ambiguity

Two lines in one phone is the most common first-week failure. Sends leave from the unexpected SIM, inbound automations attach to the wrong line, and the other SIM's carrier traffic mixes into your feed.

How to detect it: send a test message and see which number the recipient gets. Repeat after each OS update.

Mitigations. Standardize on one SIM per gateway phone, and label the device. If you need dual SIM, do not rely on the Android default: textbee takes simSubscriptionId on the send call, and the app lists each SIM's subscription id under Dashboard, in SIM Cards.

JSON
{
  "recipients": ["+15550142"],
  "message": "Your code is 481920",
  "deviceId": "665f1c2a9b1e4a0012ab34cd",
  "simSubscriptionId": 2
}

One sharp edge: simSubscriptionId is not validated. If the id does not match a SIM in the phone it is ignored and the message goes out from the preferred or default SIM. Nothing fails, so if the sending number matters, confirm which number the message arrived from. deviceId is the opposite: a malformed id fails the request rather than falling back, so a typo can never silently send from the wrong phone.

What breaks #2: batch pacing and carrier tolerance

Queues build up. A cron job fires a few hundred messages at once, or a device returns from an outage with a backlog. A phone emptying that queue as fast as it can looks, to a carrier, like a spam script.

Carriers filter aggressively on the consumer messaging path. CTIA's Messaging Principles and Best Practices (May 2023) describes the industry's use of filtering software, machine learning, and analytics against unwanted traffic, and separates consumer messaging from application-to-person traffic. A consumer SIM sending like an A2P campaign is the pattern those systems look for.

Symptoms: the first few dozen messages deliver, then delivery stops while the device still reports them sent. Behaviour normalizes after a quiet period.

What textbee does for you. Large sends are not dumped on the phone. The server releases them in waves paced to the device's smsSendDelaySeconds setting. Queued messages carry a dispatchDueAt timestamp, and a large send returns an estimatedCompletionAt:

JSON
{
  "data": {
    "success": true,
    "message": "SMS added to queue for processing",
    "smsBatchId": "665f1c2a9b1e4a0012ab34cf",
    "recipientCount": 420,
    "estimatedCompletionAt": "2026-09-23T14:05:00.000Z"
  }
}

That estimate holds only if the device stays online. Treat it as a plan, not a promise.

What you should do. Raise smsSendDelaySeconds on the device rather than adding sleeps in your own code, so pacing survives retries and restarts. Spread large lists across the day, and vary message bodies where the content allows it.

What breaks #3: an HTTP 200 is not a delivered message

A 200 from POST /gateway/send-sms means the batch was accepted and queued. Several hops remain, each able to fail on its own: the server queue, the push to the device, SmsManager handing off to the carrier, the carrier network, the recipient's handset. textbee tracks that with a lowercase status on every message (pending, dispatched, sent, delivered, failed, unknown) and a timestamp for each transition, and since October 2026 every batch carries a status and per-state counts of its own. The delivery status docs walk through every state and the three ways to track them.

Two things to internalize. sent is the strongest confirmation a SIM-based gateway can give on its own, because the device is confirming the hand-off. And status=sent is not the same axis as direction=sent: one is a delivery state, the other means outbound. The history endpoint accepts both, which is why direction=sent&status=failed is the query for "which of my sends failed".

Mitigations. Model delivery as asynchronous wherever it touches your product. If a user-facing action waits on an SMS, add a timeout and a fallback, and never take an irreversible action on the strength of the initial 200. Subscribe to the MESSAGE_SENT, MESSAGE_DELIVERED, and MESSAGE_FAILED webhook events so state changes reach you as they happen.

What breaks #4: battery optimization and silent stalls

Android kills background work to save power, and several OEMs layer more aggressive schedulers on top of Doze. The symptoms are distinctive: queued messages all flush the moment the screen turns on, inbound SMS go missing overnight, heartbeats stop after the phone sits idle.

The fix is two settings and a charger: keep the device on permanent power, set the textbee app to Unrestricted battery, and disable the manufacturer's sleep and autostart restrictions. The app's Device health screen (Settings in the Android app) checks each of these and shows the steps for your phone brand. Per-brand paths for Samsung, Xiaomi, Huawei, Oppo, OnePlus and Vivo are in Android SMS gateway not sending? Fix battery optimization.

Do not confuse this with a permission problem. If sends fail immediately rather than stalling, the cause is usually the SMS permission, not the scheduler. The dashboard now shows an SMS permission off badge on the device as soon as the phone reports it, before the first message fails. Android 15 and 16 made that permission stricter for sideloaded apps; the Android 15 permission guide covers the fix.

What breaks #5: inbound capture gaps

Two-way flows (reply handling, STOP keywords, OTP confirmation) fail quietly when inbound capture is not solid.

Check the boring things. Receiving is off by default: turn it on in the app, then confirm it under Devices in the dashboard or read receiveSMSEnabled from the device list endpoint. RECEIVE_SMS is granted separately from SEND_SMS. Webhook URLs must be public HTTPS: private and loopback hosts are rejected, so local development needs a tunnel.

Then close the gap. A webhook unreachable for ten minutes is ten minutes of lost inbound messages, and nothing in your logs will say so. The fix is a reconciliation sweep: walk GET /gateway/messages with direction=received, order=asc, and a cursor, then process anything your handler never saw. Cursor pagination makes that safe to run on a loop. The webhook is the fast path, the sweep is the proof. The inbound webhook guide covers the endpoint side, including signature verification.

What breaks #6: expecting cloud volume from a phone

The most expensive mistake is a planning mistake. A team tests at a handful of messages a day, launches, then pushes a marketing blast through one SIM. The device sends for a while, hits carrier filtering, and the rest arrive hours late.

Be honest about the ceiling. One phone on one consumer SIM handles hundreds to low thousands of messages per day, best spread out. That suits transactional traffic: codes, reminders, alerts, notifications. It does not suit carrier-grade bulk marketing, and no tuning changes that.

Plan limits are the other half. Checked October 2026 on the pricing page: the free plan allows one active device, 50 messages per day and 300 per month. Pro is $14.99 per month ($8.33 a month billed yearly), allows up to 5 active devices, and raises the cap to 5,000 messages per month with no daily limit. Scale is $29.99 per month ($16.67 billed yearly) for 15 devices and 25,000 messages. The monthly allowance resets on the day your plan started, not on a rolling 30-day window, so a launch-day burst does not haunt the next four weeks.

Mitigation: test at several times your expected launch-day volume, and add devices early. A second phone buys headroom, a second sender number, and a second carrier so one outage is not total. Note what it is not: there is no automatic load balancer. A send goes to the deviceId you name, or to your default device, or to the enabled device with the most recent heartbeat. Routing is your application's job, usually a small map from message type to device id plus a fallback.

What to monitor in production

A healthy gateway is boring. Alert on the things that make it stop being boring:

  • Device reachability. lastHeartbeat on the device list endpoint is the honest signal. Alert on silence, not failure. batteryInfo and networkInfo on the same object tell you why.
  • Device health, on the phone. The app's Device health screen checks permissions, notifications, battery usage, power saving and the connection to textbee, and the Activity tab shows each message from the moment the phone got it to delivery. Check both before blaming the carrier.
  • Queue age. If pending messages accumulate or dispatchDueAt keeps sliding, the phone is not draining. A device problem, not a carrier one.
  • Failure rate. Track direction=sent&status=failed over a rolling window. A spike usually means a deactivated SIM, no signal, or a permission lost to an OS update.
  • The sent to delivered ratio. Messages that reach sent and stop are the fingerprint of carrier filtering.
  • Inbound liveness. A message from a second phone, due at your webhook within a minute, beats any dashboard check.

The production readiness checklist

  • Dedicated device on permanent power, not a daily phone. A cheap secondhand Android is enough.
  • Battery optimization disabled for the textbee app.
  • SEND_SMS and RECEIVE_SMS granted, re-confirmed after every OS update.
  • Receiving enabled in the app if you use inbound, verified via receiveSMSEnabled.
  • smsSendDelaySeconds set to a pace you have tested on your carrier.
  • SIM, number, and owner documented. Who swaps the SIM, who reboots the phone.
  • Heartbeat alert for device silence, and status webhooks plus a cursor sweep as the backstop.
  • Runbook written: power, signal, permissions, plan limit, carrier balance.
  • Compliance reviewed for your message types: SMS compliance checklist.

Hybrid: your own SIM plus a cloud provider

Most teams past a certain size run both.

Your own SIM (textbee)Cloud SMS provider
Best forTransactional: codes, reminders, alerts, repliesMarketing bursts, global reach, short codes
SenderYour own number, already known to recipientsA provisioned number or short code
Throughput ceilingSet by the phone and the carrierSet by your plan
Per-message feeNone, beyond your normal SMS planPer message, per country
Operational loadYou run a phoneThe provider runs the fleet

Use the cloud provider for SLAs, short codes, burst throughput, or countries your SIM does not cover. Use your own SIM for the transactional slice, which for most products is the majority of sends. To run the backend on your own infrastructure too, see self-hosting an open source SMS gateway.

Frequently asked questions

How do I tell carrier throttling apart from a device problem?

Look at where messages stop. If they reach sent but never delivered, the device did its job and the block is downstream. If they sit at pending or dispatched, the phone is the problem: check power, battery optimization, signal, SEND_SMS.

What happens if the phone loses power mid-batch?

Messages already accepted stay queued server-side. When the device reconnects and reports a heartbeat, the queue resumes. Nothing is dropped, but the delay is real, so time-sensitive messages need a fallback.

Should I use a prepaid SIM?

Prepaid works, and many teams prefer it because there is no contract to unwind if a line gets suspended. Read the plan's terms on automated and commercial messaging first, and keep a spare activated SIM ready.

When should I stop using my own SIM?

When your volume, reach, or contracts outgrow a phone. Sustained bulk marketing, many countries, short codes, or a customer contract with an uptime SLA all say move that traffic to a cloud provider. Keeping transactional traffic on your own number while you do it is usually right.

Get more textbee in Google Search

Add textbee.dev as a preferred source. Our posts show up more often in your Google results.

Add as a preferred source