SMS Import / Export: Android SMS and MMS backups as NDJSON and ZIP
SMS Import / Export is a simple Android app that imports and exports SMS and MMS messages, call logs, contacts, and blocked numbers from and to JSON / NDJSON files.
At a glance
- What is it?
- SMS Import / Export is a Kotlin Android app that moves SMS and MMS messages, call logs, contacts and blocked numbers in and out of JSON or NDJSON files without root. It suits people switching phones or scripting message backups, but its v2 message format is not backward compatible with older app versions.
- Who is it for?
- Adopt SMS Import / Export if you want phone messages off the device in a text format you can inspect with jq, and if you are starting from version 2.x or re-exporting from a modern phone. Do not adopt it as a long-term archive format for messages without reading the version 2 migration notes, because a backup taken by a 1.x build cannot be read back by a 2.x build.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Kotlin, 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
One app for messages, call logs, contacts and blocked numbers
The project describes itself as a simple Android app that imports and exports SMS and MMS messages, call logs, contacts, and blocked numbers from and to JSON or NDJSON files. One important qualifier appears in the same sentence: contacts import and export are functional but considered experimental. Everything else is presented as the stable part of the feature set.
The audience is fairly obvious from the design. SMS Import / Export is for a person moving to a new phone, for someone keeping a searchable personal archive of text conversations, and for anyone who wants the message store in a format that a script can read instead of a proprietary database dump. Root is not required, which is the constraint that keeps it usable on a stock retail device.
The repository is written in Kotlin and packaged as an ordinary Android application with the application ID com.github.tmo1.sms_ie. It is distributed through GitHub releases, F-Droid and IzzyOnDroid, and the repository also contains an ENCRYPTION.md and a MESSAGE-FILTERING.md document, so encrypted exports and filtering of what gets exported are both part of the project rather than guesses on the reader's part.
What version 2.0.0 changed about the on-disk message format
Version 2.0.0 introduced a rewrite of the message import and export code and a new storage format named v2. Three changes define it.
First, messages are stored in a newline-delimited JSON file, always named messages.ndjson, instead of the standard JSON file used before. Second, binary MMS data is kept apart from message text and metadata. The messages.ndjson file and a data directory holding the MMS binary files copied straight from the Android filesystem with their original filenames are both placed inside a ZIP file. Third, every tag the app adds to the records is prefixed with a double underscore, for example __display_name and __parts, so that fields written by the app are distinguishable from fields belonging to the platform.
The double underscore convention is the detail that matters most if you plan to write your own tooling. A parser that expects plain platform keys will pick up app-added keys and has to know that anything starting with __ is not original device data.
Call logs and contacts did not move to the new format. The changelog states that the format for those two is currently unchanged, although it may be switched to the new format in the future.
Reading a backup with jq after you unzip it
There is a deliberate trade-off in the v2 format. Because a newline-delimited JSON file cannot contain newlines inside a record, the output is less pleasant to read by eye than the earlier pretty-printed JSON. The project points at a concrete fix: feed the file to jq, which pretty-prints each record.
~$ jq < messages.ndjsonThat command reads the file from standard input, so it works directly against an extracted messages.ndjson with nothing else on the command line. The reader should expect one formatted JSON object per line of output, with the app-added __ prefixed fields visible alongside the original message fields.
The archive layout is worth stating plainly because it is the thing to verify after an export. messages.ndjson holds the message records. The data directory holds the raw MMS binaries under their original Android filenames. Both sit inside the ZIP, so an export gives you one file to move around and a directory to open after extraction. The JSON tags the app adds are documented as __display_name and __parts among others, which means a backup is readable with standard tooling but not without a small amount of schema knowledge.
Where the APK comes from and how to check what you installed
Three distribution channels are named in the installation section, and they are not interchangeable in one respect. Releases with pre-built APK packages are on the GitHub Releases page, and the same builds are listed on F-Droid and IzzyOnDroid. Debug packages built from the latest pushed code are produced by the GitHub Actions workflow, and the instruction is to open the latest workflow run and take com.github.tmo1.sms_ie from the Artifacts section. Those are debug builds, not release builds.
Two identifiers are published so that a download can be checked before installation:
- Application ID: `com.github.tmo1.sms_ie`
- App signing certificate fingerprint (SHA-256) (for GitHub, as opposed to F-Droid, releases): `C1:05:E6:D9:67:55:42:D4:34:A9:CD:E9:DC:79:B2:49:F1:AC:0A:FC:3B:8A:AE:C7:D5:C8:20:11:36:CF:FD:BE`The fingerprint applies to GitHub releases as opposed to F-Droid releases, so a mismatch on that particular hash is not by itself evidence of a tampered file if the build came from F-Droid. Checking it is still worthwhile for a build you downloaded from a release page.
For anyone building from source, the repository points to a separate BUILDING.md file with the instructions. The build itself uses a Gradle wrapper, with gradlew and gradlew.bat at the repository root, plus build.gradle, settings.gradle and a fastlane directory for release automation.
API level gates that switch features on and off
Compatibility is the section of the documentation most worth reading before deployment, because the app runs on a wide range of devices while individual features turn on at specific API levels.
Current versions run on any phone-like Android device with API level 19 or later, which is Android 4.4 KitKat. Encryption and decryption of message data require API level 21, Android 5.0 Lollipop. Messages import and scheduled messages export require API level 23, Android 6.0 Marshmallow. Blocked numbers import and export require API level 24, Android 7.0 Nougat.
So a device on API level 19 or 20 gives you the app and its core export path, but not encryption, not message import, not scheduled export and not blocked numbers. That is a real limitation for an organisation with older handsets in the field, and it is invisible if you only test on a current phone.
The documentation also notes that some functionality depends on the actual API level of the device regardless of which build flavor is installed. Testing is done primarily on stock Android and LineageOS, and the project says it should generally run on other Android versions as well, with a note that there are a number of reports of the app running on other builds.
standard and legacy flavors differ only in dependency versions
The app ships in two product flavors, standard and legacy. The standard flavor runs only on devices with API level 24 or later and uses the latest versions of all of its dependencies. The legacy flavor runs on devices as low as API level 19, which is Android 4.4 KitKat, but uses outdated versions of many of its dependencies.
The important sentence follows: there is no difference in functionality between the two flavors, although some functionality is still tied to the actual API level of the device. The guidance is that the standard flavor should always be used except when deploying to devices below API level 24.
That is a sane arrangement. A single codebase serves both an ageing fleet and current hardware, and the only thing you give up on legacy devices is dependency freshness. What you cannot do is use legacy to unlock features, because the feature gates in the previous section are keyed to the API level of the device, not to the flavor.
Note that the flavor choice is also a security and maintenance decision. Legacy means shipping builds linked against older dependency versions, which is a trade a team should make explicitly when it has old hardware in the field, rather than as a side effect of choosing an install target.
Three ways to move a v1 message file forward, none of them risk free
The migration problem is the sharpest limitation in the project, and the documentation is honest about it: the format changes render versions of the app from 2.0.0 onward incompatible with JSON message files produced by earlier versions.
Three routes are offered. An earlier build of the app, one with a 1.x.x version number, can be used to import messages in v1 format. Where it is feasible, a current version of the app can be used to re-export the messages into v2 format. And a conversion tool for moving files from v1 to v2 exists in the repository under tools/, with the script at tools/v1-v2-convert.py and its own documentation in tools/Tools.md. The project is explicit that this tool is experimental and has not been extensively tested.
The three approaches differ in what they require from you. Re-exporting from the phone is the cleanest, because the newer app understands the new format natively, but it needs the source device and the old app available at the same time. Keeping an old 1.x build around means maintaining a second install on the device just for the import. The converter script avoids the device entirely but inherits the caveat that it is barely proven.
A useful fourth option is simply to do nothing and keep the old file as a static copy, since the older format was plain pretty-printed JSON and remains readable with any JSON tool.
Maintenance activity and the GPL-3.0 terms
The project is not archived, and the last push was on 2026-10-18. Recent tagged releases are v2.12.0 on 2026-10-09, v2.11.1 on 2026-07-31 and v2.11.0 on 2026-07-28. Two of the three landed within about a week of each other in late July, followed by a gap of more than two months before the next tag, which reads as a project that ships when there is something to ship rather than on a fixed calendar.
The license is GPL-3.0, and a COPYING file sits at the repository root alongside CHANGELOG.md and CONTRIBUTING.md. For a personal backup tool on your own phone, the licence obligations are not a practical obstacle. If you plan to modify the app and redistribute the modified build, the copyleft terms are the part to read carefully first, and that is a question for your own legal review rather than for this article.
The repository also carries an .idea directory, .github workflows, icons, an app module and a tools directory, which together suggest that the build, the icon assets and the automation are maintained in the same tree as the code. For upgrade planning, the practical point is that the message format has already broken compatibility once, at version 2.0.0, so it deserves checking rather than assuming.
Editorial conclusion
Adopt SMS Import / Export if you want phone messages off the device in a text format you can inspect with jq, and if you are starting from version 2.x or re-exporting from a modern phone. Do not adopt it as a long-term archive format for messages without reading the version 2 migration notes, because a backup taken by a 1.x build cannot be read back by a 2.x build. Before installing, check the minimum API level of your devices against the feature gates, and confirm that a backup archive really contains messages.ndjson and a data directory before you rely on it.
Frequently asked questions
Does SMS Import / Export need root on Android?
No. The project states plainly that root is not required. It is an ordinary APK with the application ID com.github.tmo1.sms_ie, installable from GitHub releases, F-Droid or IzzyOnDroid.
What file format does SMS Import / Export use for messages?
Since version 2.0.0, messages are stored in a newline-delimited JSON file always named messages.ndjson, placed inside a ZIP alongside a data directory holding the raw MMS binaries. Before that release, the app used a standard pretty-printed JSON file.
Can I open a message backup in a text editor or with jq?
Yes, but the file is not as readable by eye as the old format because a record cannot contain newlines. The project suggests piping it through jq, which pretty-prints each JSON record.
Which Android versions support importing messages and exporting blocked numbers?
Messages import and scheduled messages export need API level 23 or later, while blocked numbers import and export need API level 24 or later. Encryption and decryption require API level 21, and the app itself runs from API level 19.
Can SMS Import / Export read backups made by older app versions?
No. The version 2.0.0 format changes make files from earlier versions unreadable by current builds. The documented options are re-exporting from the source device with a current version, importing v1 files with an older 1.x.x build, or running the experimental conversion script at tools/v1-v2-convert.py.
Official sources
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.
[](https://hysenlabs.com/projects/tmo1-sms-ie)Was this useful?