The StarCat, an alien geek who is coming from the outer space ;)

SMS2API

Technical Documentation

Overview

SMS2API is a straightforward application that forwards received SMS messages to an HTTP REST API. The message data is transferred as a JSON object via HTTP as an HTTP request. The app forwards messages only while it is active, either in the foreground or in the background. It does not forward SMS messages previously stored on the device. Owing to this functionality, the app IS NOT GOOGLE PLAY COMPATIBLE, but it only requires permissions for android.permission.RECEIVE_SMS and android.permission.INTERNET, which are standard permissions.

Endpoint Requirements

The SMS2API application makes an HTTP POST request for each received message. The content type of the HTTP request is application/json, and the encoding is UTF8. Users define the HTTP endpoint through the app's UI. The application expects a response in application/json format, containing a valid JSON object, regardless of the HTTP status code. This requirement applies universally, so even in the event of status codes 401 or 500, a valid JSON response is required. For an HTTP status 200 response, the "success" counter is incremented. For HTTP status 401 or 500 responses, the "failed" counter is incremented in the application UI.

JSON Object

The posted JSON object includes all received SMS data along with two additional fields to facilitate integration: apiKey and userDefinedId. These are simple string values located at the root level of the JSON object. As indicated by their names, these fields are intended for authentication and device-identification purposes, but you are welcome to use them for other purposes as needed for your specific case. The application does not check or modify the values of these fields.

Note: These string fields are stored in the application's local storage as plain text!

{
    apiKey: 'weiY7eirrohQu2tiOof9iehe',
    messageBody: 'Blablabla',
    originalMessage: {
        mSubId: 3,
        mWrappedSmsMessage: {
        mDataCodingScheme: 0,
        mEncodingType: 1,
        mIsStatusReportMessage: false,
        mMti: 0,
        mProtocolIdentifier: 0,
        mReplyPathPresent: false,
        mStatus: 0,
        mVoiceMailCount: 0,
        messageClass: 'UNKNOWN',
        absoluteValidityPeriod: 0,
        mIndexOnIcc: -1,
        mIsEmail: false,
        mIsMwi: false,
        mMessageBody: '2',
        mMessageRef: 0,
        mMwiDontStore: false,
        mMwiSense: false,
        mOriginatingAddress: [Object],
        mPdu: [Array],
        mScAddress: '+36709996511',
        mScTimeMillis: 1633424319000,
        mStatusOnIcc: -1,
        mUserData: [Array],
        mwiCount: 0,
        mwiType: -1,
        relativeValidityPeriod: 0
        }
    },
    originatingAddress: '+36705559955',
    serviceCenterAddress: '+36709996511',
    timestampMillis: 1633424319000,
    userDefinedId: 'motoc'
}

As this example JSON object illustrates, the originalMessage object encapsulates the entire Android-provided SMS object.

You can learn more about the represented data at https://developer.android.com/reference/android/provider/Telephony.Sms.Intents

The outer "wrapper" object also provides the most important fields at the root level. These fields are as follows:

  • messageBody is the content of the received message.
  • originatingAddress represents the sender's phone number.
  • serviceCenterAddress is the phone number of the SMS service center.
  • timestampMillis indicates the time when the Android system received the message.
  • apiKey is a simple, additional string value for extra purposes.
  • userDefinedId is another simple, additional string value for extra purposes.

Example server

var http = require('http')
http.createServer( async (req, res) => {

    // Set the proper response type
	res.setHeader('Content-Type', 'application/json')

	try {
	    
        // Create a buffer for received chunks
		const buff = []
		for await (const chunk of req) buff.push(chunk)
		
        // Get the data object & create a JSON
		const data = Buffer.concat(buff).toString()
		const message = JSON.parse(data)
		
        console.info("Message received: ", message)
		
		// ...
		// process the received message, do your business logic
		// in case of no error, return with status code 200
        res.writeHead(200, {'Content-Type': 'application/json'})
		res.write(JSON.stringify({message: `Message has been stored.`}))
		
	} catch (e) {
		
		// in case of any error, we are going to return with 500
		res.writeHead(500, {'Content-Type': 'application/json'})
		res.write(JSON.stringify({message: `Oops! Something went wrong!`}))
		
	} finally {
		res.end()
	}
	
}).listen(8080)

Appendix

The SMS2API app does not delete the received messages; it merely forwards them. Due to this design, consider implementing some automated message deletion tasks. Some Android phones offer this feature out-of-the-box, while others do not. We use Textra, a third-party texting app, which includes a feature to delete old messages. However, Textra is not perfect for this task as it does not delete unread messages; these must be deleted manually. If you find a better solution for this issue, please let us know!

SMS2API is designed to fulfill a very specific need. Consequently, it may lack certain features that you require. We have tested the app for this very specific use case, so it may be buggy when used differently. Please let us know if you find any bugs, and we will address them. We are also open to adding new features; let us know what you would like us to include. You can easily reach us at init@starcat.dev.

🤓 Happy Integration!