Skip to content

Read a mobile-money message

Turn a pasted or forwarded mobile-money SMS into clean fields, and catch the ones that look fake, before you credit anyone.

Customers paste payment confirmations as proof. The formats vary by network and country, amounts come with currency codes and separators, and some messages are forged. This script makes two calls: extraction fills a template with a confidence per field, then a decision checks the message against the payment you expected.

Terminal window
pip install requests
export OLAVERSE_KEY="your-key" # from https://console.olaverselab.com
python momo.py
momo.py
import os
import requests
URL = os.environ.get("OLAVERSE_URL", "https://api.olaverselab.com")
HEADERS = {"Authorization": f"Bearer {os.environ['OLAVERSE_KEY']}"}
def call(path, body):
r = requests.post(f"{URL}{path}", json=body, headers=HEADERS, timeout=120)
r.raise_for_status()
return r.json()
TEMPLATE = {
"direction": ["sent", "received"], # pick one
"amount": "number",
"currency": "verbatim-string", # copied exactly as written
"counterparty": "verbatim-string",
"fee": "number",
"reference": "verbatim-string",
"date": "date", # returned as YYYY-MM-DD
}
FIELD_SURE = 0.95 # field confidence to accept without a person checking it
CHECK_SURE = 0.9 # probability for the match and genuineness checks
def check_payment(sms, expected):
extracted = call("/v1/extract", {"template": TEMPLATE, "text": sms,
"instructions": "Dates in this message are day/month/year."})
if extracted["data"] is None: # the model's answer wasn't valid JSON (rare, not billed)
return "review", extracted
checks = call("/v1/decide", {
"state": {"message": sms, "extracted": extracted["data"], "expected": expected},
"questions": {
"matches": {"type": "noul",
"instructions": "The payment in the message matches the expected amount, currency and payee."},
"genuine": {"type": "noul",
"instructions": "The message reads like a real network confirmation, not an edited or forged one."},
},
})["answers"]
fields_sure = all(c >= FIELD_SURE for c in extracted["confidence"].values() if c is not None)
passed = fields_sure and checks["matches"]["noul"] >= CHECK_SURE and checks["genuine"]["noul"] >= CHECK_SURE
return ("confirmed" if passed else "review"), {"fields": extracted["data"], "checks": checks}
if __name__ == "__main__":
sms = ("Your MTN MoMo transfer of GHS 250.00 to KOFI MENSAH was successful. "
"Fee GHS 2.50. Ref 88213374. 14/10/2025")
expected = {"amount": 250, "currency": "GHS", "payee": "KOFI MENSAH"}
outcome, details = check_payment(sms, expected)
print(outcome) # "confirmed": credit it, keyed on the reference so a repeat can't credit twice
print(details) # "review": send it to a person with these details

verbatim-string copies a value exactly as written, so a reference or name is never “tidied”. ["sent", "received"] makes the model pick one.

What /v1/extract returns for the sample message. Values are illustrative.

{
"model": "mist-extract-1.0",
"data": {"direction": "sent", "amount": 250, "currency": "GHS", "counterparty": "KOFI MENSAH",
"fee": 2.5, "reference": "88213374", "date": "2025-10-14"},
"confidence": {"direction": 0.991, "amount": 0.993, "currency": 0.996, "counterparty": 0.978,
"fee": 0.997, "reference": 0.985, "date": 0.990},
"usage": {"documents": 1, "pages": 1}
}

Every field clears 0.95 and both checks pass, so the script prints confirmed.

  • Field confidence is calibrated: at 0.95 (FIELD_SURE) a field is right about 95 times in 100. Below that, a person checks.
  • Extraction and judgement are separate calls. Extraction reads what the message says; the decision judges whether to believe it.
  • Credit on confirmed, keyed on reference, so the same SMS pasted twice credits once. Send review to a person with the details the script prints.
  • Screenshots instead of text: send "image": "data:image/png;base64,..." in place of "text".
  • Bank and card alerts: the same approach works; add "balance": "number" or "account_last4": "verbatim-string" to TEMPLATE.
  • A day’s messages: POST /v1/extract/batch takes 16 per call; only the ones extracted are billed.