2 providers are having issues right now. Some verifications may fail. Status →
API REFERENCE · v1

Verify payments API

A single REST endpoint that confirms Ethiopian transactions. Send a transaction reference or a full receipt URL, with a bank code when required, to receive a normalized verification object across CBE, Telebirr, M-Pesa, and every other supported provider.

Previously verified transactions return the same data shape with cached: true and a confirmation object that tracks how many times the payment has been checked.

BASE https://pay.lubaak.com/api/v1

Authentication

/v1/verify works without any credentials - anonymous calls are permitted for evaluation, subject to a stricter rate limit (see Rate limits). Every other endpoint (saved accounts, token management) requires a bearer token.

Create an account at POST /auth/register, or exchange existing credentials at POST /auth/login - both return a personal access token in the response body. Send it on every subsequent request:

Authorization: Bearer LNjMOFktw2rGV5F8u7uAxxxxxxxxxxxxxxxxxxxx

Authenticated requests use your plan's rate limits and appear in your account usage history.


GET POST /v1/verify

Verifies a single transaction. Accepts either a query string (GET) or a JSON body (POST) - same parameters either way. Supply either url or reference; the rest are optional enrichments.

Parameters

Parameter Type Required Description
url string conditional Full receipt URL from the customer's SMS or banking app. Required unless reference is supplied.
reference string conditional Transaction or FT reference. Required unless url is supplied. Some institutions also need bank.
bank string conditional Institution code - cbe, cbebirr, boa, mpesa, awash, dashen, telebirr, zemen, wegagen, nib, siinqee, abay, amhara, berhan, hibret, oromia, ahadu, kaafiebirr, ahaduebirr, coopebirr, nibebirr, wegagenebirr, siinqeeebirr, ebirr, zamzam. See Supported banks.
phone string conditional Required for some mobile wallets when verifying by reference.
suffix string optional Additional digits shown on certain receipts, when required by the institution.
settlement_account string optional Account number or phone the payment should have landed in. Adds a settlement_match object to the response.
expected_amount number optional If set, the result flags any mismatch against the settled amount in amount_match.

Try it

Response - · -
// hit Send to try a live request

The verification object

Every successful call returns the same top-level shape, regardless of the underlying institution. The data object holds bank-specific fields.

FieldTypeDescription
valid boolean true only when the transaction exists and has settled.
bank string Echo of the institution code that was checked.
status enum verified · invalid · pending.
cached boolean true if this payment was verified before and returned without a fresh institution lookup.
data object Bank-specific transaction fields. See Data fields below.
confirmation object Present on cached responses. Tracks how many times the transaction has been independently verified.
settlement_match object Present when settlement_account was provided. Indicates whether the receiver matches.
amount_match object Present when expected_amount was provided. Compares expected vs. settled amounts.

Response format

Verified transaction

200 OK · status: verified
{
  "valid":  true,
  "status": "verified",
  "bank":   "boa",
  "cached": false,
  "data": {
    "sender_name":      "ABEBE BEKELE",
    "sender_account":   "1******77",
    "receiver_name":    "ACME TRADING PLC",
    "receiver_account": "1**********08",
    "receiver_bank":    "Commercial Bank of Ethiopia",
    "sent_amount":      "10.00 ETB",
    "service_charge":   "0.06 ETB",
    "tax_vat":          "0.01 ETB",
    "total_paid":       "10.07 ETB",
    "transfer_date":    "14/06/26 12:46",
    "transaction_id":   "FT26166XLKCN",
    "transaction_type": "Other Bank Transfer",
    "reason":           "P2P Transaction"
  }
}

Pending (institution temporarily slow)

When an institution is temporarily slow or unreachable, a soft pending status is returned instead of a hard error. Retry shortly.

200 OK · status: pending
{
  "valid":  false,
  "status": "pending",
  "bank":   "cbe",
  "cached": false,
  "data": {
    "error": "Bank service is temporarily unreachable. Please try again in a moment."
  }
}

Invalid transaction

Not-found or malformed transactions are still a 200 OK - the bank was reachable, it just doesn't recognize the reference. status is invalid and valid is false.

200 OK · status: invalid
{
  "valid":  false,
  "status": "invalid",
  "bank":   "boa",
  "cached": false,
  "data": {
    "transaction_id": "",
    "error":          "Invalid reference number"
  }
}

Repeat verification

When a transaction was verified before, the data object is identical but cached is true and a confirmation object is added.

200 OK · repeat check
{
  "valid":  true,
  "status": "verified",
  "bank":   "boa",
  "cached": true,
  "data":   { ... },
  "confirmation": {
    "confirmed_count":    4,
    "first_confirmed_at": "2026-06-01T09:12:00+03:00",
    "last_confirmed_at":  "2026-06-15T14:33:00+03:00"
  }
}

Settlement match

When settlement_account is provided, the response includes a settlement_match object with matched and match_confidence.

200 OK · with settlement_account
{
  "valid": true,
  "data":  { ... },
  "settlement_match": {
    "matched":          true,
    "match_confidence": "high"
  }
}

Data fields

All fields live inside data. Not every field is present for every bank - omitted fields are absent from the object rather than null.

FieldTypeBanksDescription
sender_name string All Full name of the payer / debit account holder.
sender_account string All Masked or partial account number of the sender.
receiver_name string All Full name of the beneficiary / credit account holder.
receiver_account string All Masked or partial account / phone of the receiver.
receiver_phone string cbe, cbebirr, mpesa, telebirr Receiver phone number (international format).
receiver_bank string awash, boa, cbe Destination bank name (inter-bank transfers).
sent_amount string All Amount credited to the receiver, with currency suffix.
service_charge string All Bank service / commission fee.
tax_vat string All 15% VAT on the service charge.
total_paid string All Total debited from sender including fees.
transfer_date string All Transaction timestamp. Format varies by bank.
transaction_id string All Unique transaction reference for this payment.
transaction_type string boa, cbe, dashen, awash Type of transaction (transfer, topup, etc.).
transaction_status string cbe Bank transaction status (e.g. COMPLETED).
reason string cbe, awash, boa, telebirr, mpesa Payment reason or narrative.
channel string cbe, telebirr, cbebirr Payment channel used (e.g. ANDROID, IOS).
invoice_no string zemen Invoice number.
receipt_number string cbebirr Receipt number.
order_id string cbebirr Order ID.
receipt_no string mpesa Receipt number.
service_type string dashen Service type label.
narrative string dashen Free-text narrative.
transfer_ref string dashen Secondary transfer reference.
error string All (when invalid) Human-readable reason when valid is false.
confirmation.confirmed_count integer Repeat checks Number of times this transaction has been verified.
confirmation.first_confirmed_at datetime Repeat checks ISO 8601 timestamp of the first verification.
confirmation.last_confirmed_at datetime Repeat checks ISO 8601 timestamp of the most recent verification.
settlement_match.matched boolean With settlement_account true if the receiver account matches.
settlement_match.match_confidence string With settlement_account high or none.

Supported institutions

Pass the bank code with references when required. Methods: reference = transaction reference, link = full receipt URL.

InstitutionCodeMethods
Commercial Bank of Ethiopia
cbe reference · link
CBE Birr
cbebirr reference (+ phone) · link
Bank of Abyssinia
boa reference · link
Telebirr
telebirr reference · link
M-Pesa Ethiopia
mpesa reference · link
Dashen Bank
dashen reference · link
Awash Bank
awash link · reference
Wegagen Bank
wegagen reference · link
Zemen Bank
zemen reference · link
NIB International Bank
nib reference · link
Siinqee Bank
siinqee reference · link
Abay Bank
abay reference
Amhara Bank
amhara reference · link
BR Berhan Bank
berhan reference · link
Hibret Bank
hibret link
Oromia Bank
oromia reference · link
Ahadu Bank
ahadu link
Kaafi Ebirr
kaafiebirr reference · link
Ahadu Ebirr
ahaduebirr reference · link
Coop Ebirr (COOPay)
coopebirr reference · link
NIB Ebirr
nibebirr reference · link
Wegagen Ebirr
wegagenebirr reference · link
Siinqee Ebirr
siinqeeebirr reference · link
eBirr (generic)
ebirr reference · link
Zamzam Bank
zamzam reference · link

Rate limits

Only /v1/verify is rate-limited; every other endpoint is unlimited. See Pricing for public tiers.

TierLimitScope
Anonymous (no bearer token)10 requests / hourPer IP address
Free (registered API key)50 requests / hourPer user account
Enterprise (custom)Custom or unlimitedAssigned by admin

When exceeded, the API returns 429 Too Many Requests with a JSON body explaining the limit. Authenticated users without credits receive 402 Payment Required. Sign in for higher limits on paid plans.


Request examples

cbe Commercial Bank of Ethiopia
Receipt link
POST /api/v1/verify
Content-Type: application/json

{"url": "https://…receipt-link-from-customer…", "bank": "cbe"}
Reference
POST /api/v1/verify
Content-Type: application/json

{"reference": "v2-hfHCxyUdnBt2Pa0OOW3F", "bank": "cbe"}
cbebirr CBE Birr
Receipt link
POST /api/v1/verify
Content-Type: application/json

{"url": "https://…receipt-link-from-customer…", "bank": "cbebirr"}
Reference + phone
POST /api/v1/verify
Content-Type: application/json

{"reference": "ABC1234567", "bank": "cbebirr", "phone": "0911234567"}
boa Bank of Abyssinia
Receipt link
POST /api/v1/verify
Content-Type: application/json

{"url": "https://…receipt-link-from-customer…", "bank": "boa"}
Reference + suffix
POST /api/v1/verify
Content-Type: application/json

{"reference": "FT26166XLKCN", "bank": "boa", "suffix": "05677"}
telebirr Telebirr
Receipt link
POST /api/v1/verify
Content-Type: application/json

{"url": "https://…receipt-link-from-customer…", "bank": "telebirr"}
Reference
POST /api/v1/verify
Content-Type: application/json

{"reference": "DF98Q6QQRM", "bank": "telebirr"}
zemen Zemen Bank
Receipt link
POST /api/v1/verify
Content-Type: application/json

{"url": "https://…receipt-link-from-customer…", "bank": "zemen"}
Reference
POST /api/v1/verify
Content-Type: application/json

{"reference": "94497018108ATWR2520600HM", "bank": "zemen"}
awash Awash Bank
Receipt link
POST /api/v1/verify
Content-Type: application/json

{"url": "https://…receipt-link-from-customer…", "bank": "awash"}
dashen Dashen Bank
Receipt link
POST /api/v1/verify
Content-Type: application/json

{"url": "https://…receipt-link-from-customer…", "bank": "dashen"}
Reference
POST /api/v1/verify
Content-Type: application/json

{"reference": "641OBTS2518100WH", "bank": "dashen"}
mpesa M-Pesa Ethiopia
Receipt link
POST /api/v1/verify
Content-Type: application/json

{"url": "https://…receipt-link-from-customer…", "bank": "mpesa"}
Reference
POST /api/v1/verify
Content-Type: application/json

{"reference": "MP12345ABC", "bank": "mpesa"}

Errors

Most verification outcomes - including invalid or not-found transactions - are still a 200 OK with valid: false; check status and data.error rather than the HTTP code. These are the actual non-200 responses across the API:

HTTPMeaningDescription
401 Unauthenticated Missing or invalid bearer token on an endpoint that requires one - everything except /v1/verify, /auth/register, and /auth/login.
404 Not found Token ID or saved-account ID in the URL doesn't belong to the caller.
422 Validation error On /v1/verify: missing url or reference, unknown bank, or required fields for the selected institution. On /auth/*: standard field validation.
402 Insufficient credits Authenticated user has no credits left. Top up at /dashboard/billing.
429 Rate limited /v1/verify only. Anonymous callers exceeded 10 requests/hour - see Rate limits.
502 Verification error /v1/verify only. We could not read the institution response. Try a different reference or receipt link.

Register & login

Both endpoints are public and return a personal access token to use as a bearer token on every other endpoint.

Register

POST /auth/register
Request body
{
  "name":                  "Abebe Kebede",
  "email":                 "abebe@example.com",
  "password":              "secret123",
  "password_confirmation": "secret123"
}
201 Created
{
  "user": { "id": 1, "name": "Abebe Kebede", "email": "abebe@example.com" },
  "token": "LNjMOFktw2rGV5F8u7uAxxxxxxxxxxxxxxxxxxxx"
}

Log in

POST /auth/login

Body: email, password. Returns the same { user, token } shape as register, or 422 with "The provided credentials are incorrect."

Current user

GET /auth/me auth required

Returns { id, name, email } for the authenticated token.

Log out

POST /auth/logout auth required

Revokes the token used on the request. Returns { "message": "Logged out." }.


Personal access tokens

Issue additional tokens (e.g. one per device or integration) without re-authenticating. All three routes require a bearer token.

MethodPathDescription
GET/auth/tokensList this user's tokens - id, name, last_used_at, created_at, expires_at (no plaintext token value).
POST/auth/tokensBody: { "name": "my-device" } for a non-expiring token, or add "expires_at": "2026-12-31" for a dated key. Returns 201 with the plaintext token - shown only this once.
DELETE/auth/tokens/{id}Revokes one token by id. 404 if it doesn't belong to the caller.

Saved accounts

A user's own address book of beneficiary accounts - handy for pre-filling settlement_account on repeat payments. All routes require a bearer token and are scoped to the caller; there's no cross-user access.

MethodPathDescription
GET/accountsList saved accounts, sorted by name.
POST/accountsBody: name, account_number, provider (all required). Returns 201.
PUT/accounts/{id}Same fields, all optional - only supplied ones are updated.
DELETE/accounts/{id}Removes the saved account. 404 if it doesn't exist for this caller.
200 OK · GET /accounts
[
  {
    "id": 3,
    "name": "Rent",
    "account_number": "1000123456789",
    "provider": "cbe",
    "created_at": "2026-06-01T09:12:00.000000Z"
  }
]
Have questions? Talk to us →