Open-source project
bogkonstantin/android_income_sms_gateway_webhook avatar
bogkonstantin/android_income_sms_gateway_webhook

An Android app that posts every incoming SMS to a URL you choose

Simple Android incoming SMS to URL forwarder

697 stars192 forksJavaMIT

At a glance

What is it?
android_income_sms_gateway_webhook is a small Android app that forwards incoming SMS messages to a URL as JSON over HTTP POST, with sender filtering by exact number, wildcard or regular expression, exponential backoff on failure, and an optional store of messages that exhausted their retries. The interesting part of the documentation is not the feature list but the explanation of why it is not on the Play Store and why Play Protect flags it.
Who is it for?
Adopt this app if you need to get SMS content into a script, a home automation system or a Telegram bot from a phone you control, since the forwarding is a plain HTTP POST you can point at anything. Skip it if you need a Play Store install, since the documentation explains the app is not eligible under the SMS and Call Log permissions policy, and if you need to receive RCS messages, because the app only handles SMS and the documentation explains why RCS cannot be supported.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 132 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The core loop is small and the feature list is about what happens on failure

This is a free, open-source Android app that automatically forwards incoming SMS messages to a specified URL as JSON via HTTP POST.

The core behaviour is one line in the documentation: every incoming SMS will be sent immediately to the provided URL. The configuration is a sender phone number or name and a URL, and the sender has to match the number or name you see in the SMS messenger app. If you want to send any SMS to the URL, use the asterisk symbol as a name.

The rest of the feature list is about failure handling and about narrowing what gets forwarded. You can forward SMS from specific numbers, from all senders, or from senders matching a regular expression. Failed requests are retried with exponential backoff. Messages that exhaust all retries can optionally be stored so you can re-send them later. The payload includes sender, message, timestamp, SIM slot and more. Messages can be forwarded directly to Telegram bots or channels. There is an optional heartbeat ping so an external monitor can alert you if the phone goes offline. And there is a built-in test message sender and an error log viewer.

No cloud services or user registration are required, which is the design decision that makes the rest of the documentation make sense. The app is a component on your phone that talks to a URL you chose, and there is no service in the middle that could see your messages or go down.

The project is Java, MIT licensed, with the last push on 2026-06-13. The releases are v2.4.0 from 2026-06-10, Version 2.3.0 from 2024-04-28 and v2.2.3 from 2023-03-02.

Retry, backoff and the store of undelivered messages

The retry behaviour is specified precisely enough that you can reason about what your endpoint will see.

If the response code is not 2XX, or the request ended with a connection error, the app will try to send again up to 10 times, and that number can be changed in the parameters. The minimum first retry will be after 10 seconds, and the later wait time will increase exponentially.

There is a second condition layered on top of the first. If the phone is not connected to the internet, the app will wait for the connection before the next attempt. That distinction matters more than it looks, because a phone that is offline and a server that is returning 500 are different failures, and treating them the same is how you get a burst of ten retries the moment the phone reconnects to a network that has been down all morning.

The stored messages feature is the other half. Messages that exhaust all retries can optionally be kept, so you can re-send them later. That is what makes the app usable as a delivery mechanism rather than a best-effort one, since a server outage of an hour does not mean you lose the messages that arrived during it.

For a webhook receiver, the practical implication is that your endpoint should be idempotent. With up to ten attempts per message and a store of undelivered ones, a message can arrive more than once, and the documentation does not describe a deduplication identifier being sent. If your endpoint counts messages rather than processing them, you will overcount.

The heartbeat is the mirror image of the retry logic, aimed at the failure mode where the phone itself goes away. An optional periodic ping to your URL tells you that the forwarder is still alive, and it is the thing to point a monitoring check at.

Sender and text filters, and the detail that they fail in opposite directions

The filtering has two independent axes and they behave differently on a bad pattern, which is the detail to understand before you rely on either.

By default the sender is matched exactly, or you use the asterisk to catch every sender. Ticking the option to treat the sender field as a regular expression switches it to Java regular expression matching, the same flavour as the text filter. The expression is tested against the incoming sender as a substring match, so it matches when the pattern occurs anywhere in the sender, and the example given is that a pattern matching BANK matches VM-BANK and AD-BANK, which is useful where an operator prefixes a fixed sender ID with a rotating code. You prefix the pattern to ignore case, and a bad pattern matches nothing, so it fails closed, meaning a typo never silently forwards unrelated senders. The asterisk wildcard keeps meaning any sender even with the option enabled.

The text filter is the second axis. By default a rule matches on the sender only and forwards every SMS from it. The optional text filter field narrows this by message body: leave it empty to forward everything, which the documentation calls the historic behaviour, or enter a Java regular expression and the rule forwards a message only when that expression matches the body, as a substring match, case sensitive, with the same case-ignoring prefix available. It combines with the sender filter, so you can use the asterisk sender to filter purely on content.

And here the failure modes diverge. A bad sender pattern fails closed, so nothing is forwarded. A bad text pattern is ignored, so the message is still forwarded. The rationale is stated as avoiding a typo silently dropping SMS, which is a defensible choice for a tool whose purpose is not to lose messages, but it does mean a typo in a text filter is silent in the other direction. The two settings are also asymmetric in another way, with the sender regex documented as matching anywhere in the sender and the text regex documented as case-sensitive by default while the sender one is documented with an opt-in case-insensitive prefix.

Why it is not on the Play Store, and why Play Protect warns

The installation FAQ answers the two questions every new user asks, and the answers are specific rather than defensive.

On the Play Store: Google Play's SMS and Call Log permissions policy only allows the RECEIVE_SMS permission for a short list of approved use cases, such as being the device's default SMS handler. Forwarding incoming SMS to a user-defined URL is not one of them, so an app built around that permission is not eligible for the Play Store, regardless of how it is written. The intended install routes are the GitHub releases page or F-Droid.

On Play Protect: the documentation calls this a false positive and says it is expected. Play Protect flags the app because reading incoming SMS and sending their contents to a remote URL is the same behaviour pattern used by SMS-stealing malware. Play Protect is an automated classifier, so it cannot tell that you configured the destination URL and you consented, and it errs on the side of warning. The app has no backend and no accounts, and only ever sends SMS to the URL you enter yourself, and the full source is in the repository so you can verify exactly what it does.

The installation guidance that follows is worth knowing. On the install warning, choose install anyway, or more details then install anyway. If Play Protect has already removed the app, you may need to temporarily turn off Play Protect while you install, then re-enable it. Some devices, with Xiaomi and MIUI and Samsung named, add their own harmful app prompt on top of Play Protect, and the same install anyway choice applies.

And there is the alternative. If you would rather not disable Play Protect, the F-Droid build is signed with a stable key whose reputation improves over time, which reduces how often the warning appears.

The point of the explanation is that a false positive here is structural. Any app that reads SMS and posts it elsewhere looks like malware from the outside, and the classifiers that decide that are not going to be persuaded by a source repository.

Permissions, the status bar icon and RCS

Getting the app running is a matter of permissions, and the documentation is specific about which ones and what each is for.

After installation you set up the app permissions for your phone, for example enabling Autostart if needed, and the display pop-up windows while running in the background setting from Xiaomi devices. Both of those are vendor power management features that will otherwise kill a background service, so they are the first thing to check when messages stop arriving.

The status bar indicator is the diagnostic. If at least one forwarding config is created and all needed permissions are granted, you should see an F icon in the status bar, which means the app is listening for the SMS. On Android 13 and newer the app also asks for the notification permission. SMS forwarding works without it, but the F status-bar indicator only appears once it is allowed, and if you do not see the icon, the documentation says to check that notifications are enabled for the app and that the system has not restricted it in the battery settings.

The RCS limitation is the one hard boundary. The app forwards SMS only. If some messages never arrive at your URL, the phone may be delivering them over RCS, which is Google's chat protocol used by Google Messages, instead of SMS. RCS is a different transport that does not trigger the SMS_RECEIVED broadcast the app relies on, and Android provides no public API for third-party apps to receive RCS messages, so it cannot be supported. The documented workaround is to turn off RCS in Google Messages, under RCS chats, and then messages arrive as normal SMS and are forwarded correctly.

There are two other built-in tools worth knowing about. The Test button makes a test request to the server, which is how you confirm the URL and the payload shape before waiting for a real message, and the Syslog button shows errors stored in the Logcat.

Editorial conclusion

Adopt this app if you need to get SMS content into a script, a home automation system or a Telegram bot from a phone you control, since the forwarding is a plain HTTP POST you can point at anything. Skip it if you need a Play Store install, since the documentation explains the app is not eligible under the SMS and Call Log permissions policy, and if you need to receive RCS messages, because the app only handles SMS and the documentation explains why RCS cannot be supported. Verify first that your phone still delivers messages as SMS rather than RCS, that the server returns a 2XX response so the retry logic does not fill the store with duplicates, and that you have granted the background and notification permissions the app needs to stay listening.

Frequently asked questions

How does the SMS forwarding app retry failed requests?

If the response code is not 2XX or the request ended with a connection error, the app retries up to 10 times, a number that can be changed in the parameters. The first retry is after at least 10 seconds and the wait increases exponentially, and if the phone has no internet connection the app waits for the connection before the next attempt.

Why is the app not available on Google Play?

Google Play's SMS and Call Log permissions policy only allows the RECEIVE_SMS permission for a short list of approved use cases such as being the device's default SMS handler, and forwarding incoming SMS to a user-defined URL is not one of them. The documented install routes are the GitHub releases page and F-Droid.

Why are some SMS messages not forwarded to my URL?

The app forwards SMS only, and RCS is a different transport that does not trigger the SMS_RECEIVED broadcast it relies on, with no public API for third-party apps to receive RCS messages. The documented workaround is to turn off RCS chats in Google Messages so the messages arrive as normal SMS.

Official sources

  1. bogkonstantin/android_income_sms_gateway_webhook on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bogkonstantin-android-income-sms-gateway-webhook.svg)](https://hysenlabs.com/projects/bogkonstantin-android-income-sms-gateway-webhook)

Was this useful?