Skip to content

Reporter app

Two feeds can tell us money arrived: your bank’s alert email, forwarded to us, and your bank’s alert SMS, read by an app on your own phone. Either one activates an account. Anyone above a handful of orders an hour should run both.

Download the app and install it on the phone that receives your bank’s alerts. Android only: iOS grants apps no access to SMS at all, so there is no iPhone reporter and there will not be one.

There is no Play Store listing yet. The direct download above is how to install the app today, and it is the documented fallback if Play review ever refuses it. Everything on this page is the contract the app implements, so you can also build your own reporter against the same endpoints.

Because it is not from Play, two things follow, and both are worth knowing before you install:

  • Android will warn you. Sideloading needs “install unknown apps” allowed for your browser. The warning is correct — it is telling you this file did not come from Play, which is true.
  • You may have to uninstall it when the Play build ships. This download is signed with our own upload key. Play App Signing re-signs what it delivers with a separate key that Google holds, so an install from Play will not necessarily carry the same signature as this file — and Android refuses to replace an installed app with one signed by a different key. If that happens the upgrade is uninstall-then-install, and the app’s stored pairing goes with it. Re-pair from Settings afterwards; nothing else carries over, and nothing else needs to. We intend to publish Play’s own signed build here once the app is live, which removes the problem entirely.

Email forwarding is free, needs no app, and works on an iPhone. But the delay between your bank sending the alert and the mail reaching us is not something we control or have measured. SMS arrives on the phone in the merchant’s hand. On a live ₹1 credit, a parsed credit reached the server about 160 ms after the phone received the message, with the app in the background and no battery exemptions.

  1. Dashboard → Settings → pairing code. Six digits, valid ten minutes, single use.
  2. Type it into the app.

The app calls:

Terminal window
curl -sS -X POST https://moneylanded.com/v1/devices/pair \
-H "Content-Type: application/json" \
-d '{"code":"418203","device_name":"Counter phone"}'
{ "device_token": "vpd_1f0b...", "tenant_name": "Blue Mug Co" }

No Authorization header: the code is the credential. That makes it the one guessable surface here, so it lives ten minutes, is destroyed the instant it is claimed, and the endpoint is rate limited per source address. An unknown code, an expired one and an already-claimed one all answer identically, so nothing can be learned by trying.

The device token is returned once and stored only as a hash. It is scoped to one account and revocable from Settings; revoking stops reports immediately and leaves email ingestion untouched and every credit it already reported standing.

Terminal window
curl -sS -X POST https://moneylanded.com/v1/reports \
-H "Authorization: Device vpd_1f0b..." \
-H "Content-Type: application/json" \
-d '{"messages":[{"sender":"JD-HDFCBK-S","body":"Credit Alert! Rs.498.99 credited to HDFC Bank A/c XX4558 on 18-09-25 from VPA asha@ybl (UPI 530112345678)","received_at_ms":1758212001000}]}'
{
"accepted": 1,
"rejected": 0,
"results": [
{ "rrn": "530112345678", "status": "matched", "intent_id": "int_9f2c1a77b40e6d3a5c81",
"order_id": "ORD-10432", "paid_paise": 49899 }
]
}

results is in request order, one entry per message. That is what lets the phone raise a notification naming the order — “₹498.99 received for ORD-10432” — which is how a WhatsApp seller knows to ship.

Up to 50 messages a batch. An empty messages array is a valid heartbeat. Retrying is free: credits are deduplicated on the bank’s reference number, and a retry returns the same result object as the first attempt rather than a bare “duplicate”, precisely so the notification still names the order. Of 665 real messages on one device, only 514 references were distinct — 139 arrived more than once — so this is the normal case, not an edge case.

What the app does, and what it deliberately does not

Section titled “What the app does, and what it deliberately does not”
  • A manifest-registered receiver fires on every incoming SMS, app foregrounded or not.
  • It parses on the device with the same rules the server uses, and posts only parsed credits: amount, account last four, date, payer handle, reference, sender id, receive time.
  • OTPs, debits, card spends and promotions never leave the phone. They are not uploaded and rejected; they are never sent.
  • Posts are queued and retried until acknowledged.
  • On pairing, and on demand, it scans the last 24 hours of bank credits and posts them, which backfills any outage.
  • The screen shows the pairing state and the last ten events. Nothing else.

On our side: raw message bodies are parsed in memory and never stored, never echoed back, and never put in an alert — including when parsing fails.

The app requests RECEIVE_SMS and READ_SMS.

Google Play restricts those permission groups, and lists SMS-based financial transactions — naming Unified Payments Interface explicitly — as an eligible use, subject to the permissions declaration form and review.

The same policy family says an app “may not use alternative methods (including other permissions, APIs, or third-party sources) to derive data attributed to Call Log or SMS related permissions”. A NotificationListenerService reading the SMS app’s notifications is exactly that, which is why this app does not use one and why you should be wary of anything that does.

Send yourself a small test payment. The first verified credit binds your account’s last four digits, and from then on a credit for any other account is rejected rather than matched. This is what stops somebody else’s forwarded alert ever confirming one of your orders.

Use Gmail forwarding. iOS grants apps no access to SMS at all, so there is no iPhone reporter and there will not be one.