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.
Run it
Section titled “Run it”pip install requestsexport OLAVERSE_KEY="your-key" # from https://console.olaverselab.compython momo.pyThe script
Section titled “The script”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 itCHECK_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 detailsverbatim-string copies a value exactly as written, so a reference or name is never “tidied”. ["sent", "received"] makes the model pick one.
Response
Section titled “Response”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.
How the answer is used
Section titled “How the answer is used”- 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 onreference, so the same SMS pasted twice credits once. Sendreviewto a person with the details the script prints.
Adapt it
Section titled “Adapt it”- 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"toTEMPLATE. - A day’s messages:
POST /v1/extract/batchtakes 16 per call; only the ones extracted are billed.
Document extractionTemplate types, images and limits.
Payment messages use caseThe full pipeline and benchmark figures.