Web API
cedikit can run as a small web API, so apps written in any language can use it: JavaScript, PHP, Java, Kotlin (Android), Dart (Flutter), C#, Go and others. You send JSON over HTTP and get JSON back.
pip install "cedikit[api]"
cedikit api
cedikit API at http://127.0.0.1:8000 - documentation at /docs (Ctrl+C to stop)
Open http://127.0.0.1:8000/docs for interactive documentation. Every endpoint is
listed there with examples, and a Try it out button sends real requests. The
machine-readable OpenAPI schema is at /openapi.json, which you can use to generate a client
library for your language.
Endpoints
All endpoints except / and /health take a JSON body sent with POST.
| Endpoint | Send | Get back |
|---|---|---|
GET / · GET /health |
nothing | Version and endpoint list · {"status": "ok"} |
POST /v1/phone/check |
{"number": "024 412 3456", "style": "e164"} |
Valid or not, E.164, formatted, likely network, masked, reason if invalid |
POST /v1/phone/clean |
{"numbers": ["0244123456", "244123456", …]} |
Counts plus one result per number (valid, fixed or invalid) |
POST /v1/money/parse |
{"text": "GH₵1.2k"} |
"1200.00", formatted, in words |
POST /v1/sms/parse |
{"text": "…", "sender": "MobileMoney"} |
Type, amount, fee, tax, balance, counterparty, ID… |
POST /v1/fraud/check |
{"text": "…", "sender": "0551234567", "history": […]} |
Risk (LOW/MEDIUM/HIGH), score, reasons, advice |
POST /v1/ledger |
{"messages": ["…", "…"], "sender": "MobileMoney"} |
Totals, every transaction with a category, notes |
POST /v1/fees/estimate |
{"network": "MTN", "kind": "cash_out", "amount": "500"} |
Fee, tax, total and where each number comes from |
POST /v1/ids/check |
{"value": "GHA-123456789-0"} |
Valid or not, kind, masked, region and district |
POST /v1/screenshot |
A picture as multipart/form-data (field image) |
Each message read from it, parsed and fraud-checked |
style is one of e164, local, pretty or international. kind is one of
send_same_network, send_other_network, cash_out, cash_in, receive, airtime or
merchant (see Fees). sender, history, received_at and
on are optional.
Money is always a string
Amounts come back as strings such as "1200.50", never as JSON numbers. Many languages
turn JSON numbers into floating-point values, which cannot store 0.10 exactly. Keep
amounts as strings, or convert them to your language's decimal type.
Examples
A fake alert, checked from the command line:
curl -X POST http://127.0.0.1:8000/v1/fraud/check \
-H "Content-Type: application/json" \
-d '{"text": "Cash In for GHS150.00 from AKOSUA. Avaliable balan 640.35", "sender": "0551234567"}'
{
"risk": "HIGH",
"score": 0.96,
"headline": "Very likely fake",
"reasons": [
"Sent from a personal phone number (+233 55 123 4567), not an official sender ID such as MobileMoney or T-CASH. Genuine alerts never come from personal numbers.",
"Looks like a Mobile Money alert but does not match any genuine message format.",
"Contains spelling mistakes ('Avaliable', 'balan'). Genuine alerts are machine-generated and don't have typos."
],
"checks": {"sender": false, "format": false, "spelling": false, "scam_phrases": true, "disguised_letters": true},
"advice": "Before releasing goods or cash, confirm the payment in your official Mobile Money app ..."
}
const response = await fetch("http://127.0.0.1:8000/v1/phone/check", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ number: "024 412 3456" }),
});
const result = await response.json();
console.log(result.e164, result.network); // +233244123456 MTN
$ch = curl_init("http://127.0.0.1:8000/v1/money/parse");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode(["text" => "GH₵1.2k"]),
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
echo $result["amount"]; // 1200.00
import requests
with open("screenshot.png", "rb") as picture:
result = requests.post("http://127.0.0.1:8000/v1/screenshot", files={"image": picture}).json()
for message in result["messages"]:
print(message["fraud"]["risk"], message["text"][:60])
(In Python you can also import cedikit directly, without the API.)
import 'dart:convert';
import 'package:http/http.dart' as http;
final response = await http.post(
Uri.parse('http://10.0.2.2:8000/v1/fees/estimate'), // 10.0.2.2 = your PC, from the Android emulator
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'network': 'MTN', 'kind': 'cash_out', 'amount': '500'}),
);
final fee = jsonDecode(response.body)['fee']; // a string, e.g. "5.00"
Errors
| Status | Meaning |
|---|---|
200 |
Success. An invalid phone number or ID is still a 200, with "valid": false and a reason. |
401 |
An API key is required and was missing or wrong. |
413 |
The picture is larger than 10 MB. |
422 |
Something in the request is wrong, e.g. "abc" as an amount or an unknown network. detail says what. |
501 |
Screenshots need an OCR engine that isn't installed. |
Limits
- One message: up to 5,000 characters.
- One request: up to 5,000 messages or phone numbers.
- One picture: up to 10 MB.
Privacy and security
- Nothing is stored. Each request is handled in memory and forgotten. The server does not log message text.
- Local only by default.
cedikit apilistens on127.0.0.1, so only your own computer can reach it. -
To share it on a network (e.g. to test from a phone on the same Wi-Fi), start it with
--host 0.0.0.0and set an API key:# Windows PowerShell: $env:CEDIKIT_API_KEY = "a-long-random-secret" export CEDIKIT_API_KEY="a-long-random-secret" cedikit api --host 0.0.0.0Every request (except
/and/health) must then send the key in anX-API-Keyheader. cedikit warns you if you share the API without a key. - Browsers. By default any website may call the API from a browser (CORS). To allow only your own site, setCEDIKIT_API_CORS=https://your-site.example(comma-separate several). - On the internet, put it behind HTTPS (for example a reverse proxy such as Caddy or nginx). The API key travels in a header, so it must not be sent over plain HTTP.
Hosting it yourself
The API is a standard FastAPI app at cedikit.api:app, so
any ASGI server can run it:
uvicorn cedikit.api:app --host 0.0.0.0 --port 8000 --workers 2
In your own Python code, cedikit.api.create_app(api_key=..., cors_origins=[...]) builds an
app with your settings. You can mount it inside a larger FastAPI app.
Risk ratings are indicators
A LOW fraud risk does not guarantee a payment is real. Apps built on the API should tell
users to confirm payments in the official Mobile Money app before releasing goods.