Documentation
API reference
Base URL: https://verify.qbirr.com
Stable since v1. Backwards-compatible changes are added regularly; breaking changes get a new path prefix.
Authentication
Every request needs your API key in the X-API-Key header. The key looks like abc12345.LONGRANDOMSECRET — send the whole string.
POST /api/v1/verify
Verifies one transaction. Returns verified: true only if the receipt is real, the amount matches, and the receiver name matches.
curl -X POST https://verify.qbirr.com/api/v1/verify \
-H "X-API-Key: $QBIRR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "cbe",
"ref": "FT23001234ABC",
"amount": 500,
"receiver_name": "YOUR FULL NAME",
"receiver_account": "1000017692643"
}'
import { Qbirr } from '@qbirr/sdk';
const qb = new Qbirr({ apiKey: process.env.QBIRR_API_KEY });
const r = await qb.verify({
provider: 'cbe',
ref: 'FT23001234ABC',
amount: 500,
receiverName: 'YOUR FULL NAME',
receiverAccount: '1000017692643',
});
console.log(r.verified ? '✓ ' + r.payer : '✗ ' + r.error);
from qbirr import Qbirr
qb = Qbirr(api_key=os.environ["QBIRR_API_KEY"])
r = qb.verify(
provider="cbe",
ref="FT23001234ABC",
amount=500,
receiver_name="YOUR FULL NAME",
receiver_account="1000017692643",
)
print(r.verified, r.payer)
use Qbirr\Qbirr;
$qb = new Qbirr($_ENV['QBIRR_API_KEY']);
$r = $qb->verify([
'provider' => 'cbe',
'ref' => 'FT23001234ABC',
'amount' => 500,
'receiver_name' => 'YOUR FULL NAME',
'receiver_account' => '1000017692643',
]);
echo $r['verified'] ? "✓ {$r['payer']}" : "✗ {$r['error']}";
qb := qbirr.New(os.Getenv("QBIRR_API_KEY"))
r, err := qb.Verify(ctx, qbirr.VerifyRequest{
Provider: qbirr.ProviderCBE,
Ref: "FT23001234ABC",
Amount: 500,
ReceiverName: "YOUR FULL NAME",
ReceiverAccount: "1000017692643",
})
if err != nil { panic(err) }
fmt.Println(r.Verified, r.Payer)
Request fields
| Field | Type | Notes |
|---|---|---|
| provider | string | cbe · telebirr · abyssinia · dashen · mpesa · awash · ebirr |
| ref | string | Transaction ref. For awash, paste the SMS URL or its token. For ebirr, paste the URL or "tenant/token" |
| amount | number | Expected amount in ETB. Overpayment OK; underpayment up to 10 ETB tolerated |
| receiver_name | string | Required. Case-insensitive whitespace-normalised match against the receipt |
| receiver_account | string | Required for cbe and abyssinia |
Response
{
"verified": true,
"payer": "JANE DOE",
"amount": 500,
"error": ""
}
GET /api/v1/usage
Returns the current calendar month's quota status.
curl https://verify.qbirr.com/api/v1/usage -H "X-API-Key: $QBIRR_API_KEY"
{
"used": 124,
"limit": 1000,
"remaining": 876,
"plan": "Starter",
"period_start": "2026-05-01T00:00:00Z",
"period_end": "2026-06-01T00:00:00Z"
}
HTTP status codes
| Code | Meaning |
|---|---|
| 200 | Processed. Check verified — could be true or false |
| 400 | Malformed request |
| 401 | Missing or invalid API key |
| 402 | No active plan or plan expired |
| 403 | Account suspended |
| 429 | Quota or per-IP rate limit (60 req/min) |
Provider notes
CBE cbe
Commercial Bank of Ethiopia.
Requires receiver_account.
Telebirr telebirr
Ethio Telecom mobile money.
Geo-blocked to Ethiopian IPs — handled automatically by our relay.
Awash Bank awash
Private bank.
Paste the SMS URL or just the URL token as ref.
Dashen Bank dashen
Super App.
M-Pesa mpesa
Safaricom Ethiopia.
Geo-blocked to Ethiopian IPs — handled automatically by our relay.
Bank of Abyssinia abyssinia
Private bank.
Requires receiver_account.
eBirr ebirr
COOPay · KAAFI · Nib · Wegagen · Ahadu.
Paste the URL or "tenant/token" as ref.
Official SDKs
Node.js
Node 18+ · zero deps
npm install @qbirr/sdk
Python
Python 3.8+ · requests
pip install qbirr
PHP
PHP 7.4+ · curl + json
composer require qbirr/sdk
Go
Go 1.21+ · stdlib only
go get github.com/qbirr/sdk-go
For Ruby, .NET, Java, Rust, or anything else — see the cURL example above; it's the same HTTP shape everywhere.
Best practices
- Verify server-side. Never put your API key in browser/mobile code.
- Store the ref on the order. qbirr enforces uniqueness, but checking on your side too prevents accidental double-verifies.
- Don't auto-refund on
verified: false— false negatives are possible if a provider's receipt page is briefly down. - Cache the verified result in your order record. Don't re-call
/verifyfor the same payment.