file://.
Browsers block API calls from file://. Start the stack
(docker compose up -d) and open
http://localhost:1111/docs.
About
Verification API for Telebirr and CBE (Commercial Bank of Ethiopia) receipts. Each call is independent — no duplicate-detection state is kept.
🔐 Authentication: POST /api/check/*,
POST /api/verify/*, and legacy /check//verify
require an API key. Paste yours in the API key field above to use the live "Try it" forms.
Get one by signing up (free 1-day trial). Send via:
Authorization: Bearer pk_live_...
/api/check/*— pure parse, no receiver validation/api/verify/*— parse and match receiver name/phone (Telebirr) or name/account (CBE)- Error model:
{ "ok": false, "error": "..." }
GET/health
Liveness probe + deployed code version. Use code_version after every deploy to confirm the new code is live.
Request
(no body)
200 response
{
"status": "ok",
"timestamp": "2026-05-26T20:00:00.000Z",
"domain": null,
"code_version": "stateless-bank-split-node-2026-05-25",
"version": "2.0.0",
"telebirr_proxy": false
}
Try it
GET/banks
Lists the banks this build supports.
200 response
{ "supported_banks": ["telebirr", "cbe"] }
Try it
POST/api/check/telebirr
Pure parse — fetches the Telebirr receipt page and returns parsed fields. No receiver validation. Accepts a receipt id (DDQ18SQ4KF), a full receipt URL, or the full SMS text containing the link.
Request body
{ "url": "DDQ18SQ4KF" }
200 response (shape)
{
"ok": true,
"bank": "TELEBIRR",
"receipt_id": "DDQ18SQ4KF",
"payment_link": "https://transactioninfo.ethiotelecom.et/receipt/DDQ18SQ4KF",
"amount": 247.5, // settled (what the receiver got)
"total_paid_amount": 250, // settled + service_fee + vat
"settled_amount": 247.5,
"service_fee": 2.5,
"vat": 0,
"name": "ABEBE KEBEDE",
"phone": "251911****",
"sender_name": "TIGIST ALEMU",
"sender_phone": "251922****",
"status": "Completed",
"payment_date": "26-04-2026 19:08:24",
"payment_date_iso": "2026-04-26T19:08:24",
"amount_text": "247.5 Birr",
"total_paid_amount_text": "250 Birr",
"auto_eligible": true,
"issues": []
}
Errors
400 missing fields ·
502 upstream fetch/parse failed ·
504 upstream timeout
Try it
POST/api/verify/telebirr
Parses the receipt and validates that it was paid to you by matching receiver_name and receiver_account. (For Telebirr, receiver_account = the receiver's phone number. The legacy field name receiver_phone is still accepted.) Returns 422 if the receipt parses but the receiver doesn't match.
Request body
{
"url": "DDQ18SQ4KF",
"receiver_name": "ABEBE KEBEDE",
"receiver_account": "251911000000"
}
200 response
Same shape as /api/check/telebirr, with verified: true and a populated verification object.
422 response
{
"status": "success",
"bank": "telebirr",
"data": { ... },
"verified": false,
"verification": {
"verified": false,
"issues": ["receiver_name_mismatch"]
}
}
Try it
POST/api/check/cbe
Pure parse for CBE. url can be any of:
- Web receipt URL —
https://mbreciept.cbe.com.et/FT26139QTLZG-00260493 - Mobile-app URL —
https://apps.cbe.com.et:100/?id=FT26136VTVLH00260493 - USSD short URL —
https://mbreciept.cbe.com.et/fHCxyTN0v4QJs2IAjF - Just the receipt id —
FT26139QTLZG-00260493or the short code alone - Bare FT — pair with
/api/verify/cbe+receiver_account - The full SMS text — we'll pull the URL out
Request body
{ "url": "FT26121GRBSB12345678" }
200 response (shape)
{
"ok": true,
"bank": "CBE",
"receipt_id": "FT26121GRBSB12345678",
"payment_link": "https://mbreciept.cbe.com.et/FT26121GRBSB12345678",
"amount": 1000,
"amount_debited": 1000,
"service_fee": 0,
"vat": 0,
"currency": "ETB",
"status": "COMPLETED",
"name": "ABEBE KEBEDE",
"account": "1000***0778",
"sender_name": "TIGIST ALEMU",
"sender_account": "1000***1234",
"payment_date": "2026-05-01T17:08:24+03:00",
"auto_eligible": true,
"issues": []
}
Try it
POST/api/verify/cbe
Parse + receiver match for CBE. receiver_account is required and is also used to complete bare FT… IDs (the API auto-appends the last 8 digits of receiver_account when the full reference isn't provided).
Request body
{
"url": "FT26121GRBSB",
"receiver_name": "ABEBE KEBEDE",
"receiver_account": "1000000000778"
}
Responses
200 verified ·
422 parsed but receiver mismatch ·
400 missing fields ·
502 upstream
Try it
GET/stats
Aggregate usage stats for the running process — total requests, error rate, per-bank counts. Resets on restart unless USAGE_TRACK_ENABLED=true and the JSONL log is persisted to a volume.
200 response (shape)
{
"uptime_seconds": 1234,
"total_requests": 0,
"by_bank": { "telebirr": 0, "cbe": 0 },
"by_status": { "200": 0, "400": 0, "422": 0, "502": 0 }
}
Try it
POST/check legacy
Old all-in-one endpoint. Kept for back-compat with existing integrations. Prefer /api/check/* and /api/verify/* in new code.
Request body
{
"bank": "telebirr",
"url": "DDQ18SQ4KF",
"receiver_name": "ABEBE KEBEDE",
"receiver_phone": "251911000000"
}