Verify Telebirr Payments via API in 5 Minutes
Quick Answer: Telebirr verification confirms that a customer's Telebirr transaction actually landed in your business wallet — without you opening the app or asking for screenshots. ShegerPay provides a non-custodial API: the customer pays your Telebirr account directly, your backend calls sp.verify({ transactionId, provider: 'telebirr' }), and you get a verified result in under two seconds plus a webhook for async confirmation.
What is Telebirr and why verification matters
Telebirr is Ethio Telecom's mobile money wallet — the dominant mobile-money rail in Ethiopia, with tens of millions of active users. Every Ethiopian SaaS, e-commerce store, and service business accepts Telebirr because customers expect it. The problem is what happens after the customer hits send.
A merchant receives a Telebirr SMS that looks like You have received ETB 1,500.00 from +2519XXXXXXXX. Ref: AB12CD34EF. That SMS is the entire confirmation. To match it to an order, a human has to read the SMS, find the matching order in the dashboard, confirm the amount and reference are correct, and manually unlock the customer's product. At ten transactions a day this is annoying; at a hundred it's a full-time job; at a thousand it's broken.
Verification matters because it's the seam between "money arrived" and "product unlocked." Get it wrong and you either lose revenue (legitimate customers waiting) or get defrauded (fake screenshots, manipulated SMS forwards).
The manual reconciliation problem
Most Ethiopian merchants run one of three broken workflows today:
- Screenshot-based. Customer sends a Telebirr screenshot via Telegram. Staff eyeballs it. Highly fakeable — there are public templates for forging Telebirr screenshots.
- SMS-forward. Merchant forwards every Telebirr SMS to a shared inbox or Telegram group. Staff manually matches SMS to orders. Slow, error-prone, and impossible to audit.
- End-of-day reconciliation. Staff logs into the Telebirr business portal once a day, exports a CSV, and matches it against orders. Customers wait hours or overnight to receive what they paid for.
All three break down because the verification step is human. The fix is to make it API.
Setting up ShegerPay for Telebirr (step-by-step)
- Sign up. Create an account at shegerpay.com. The free tier includes 100 verifications — enough to test in production.
- Link your Telebirr business wallet. In the dashboard, go to Providers → Telebirr and paste your Telebirr business shortcode and the registered phone number. ShegerPay reads transaction records under your account; it never holds your funds.
- Grab an API key. From Settings → API Keys, generate a
sk_live_...key. Store it as an environment variable; never commit it. - Install the SDK. ShegerPay ships official SDKs for JavaScript/TypeScript, Python, PHP, Go, Ruby, Java, C#, Kotlin, Swift, Dart, and a WordPress plugin. Pick the one matching your stack.
- Add a verify call. From your order-confirmation endpoint, call
sp.verify(...)with the transaction reference the customer pasted (or that you captured via deep link). - Add a webhook listener. ShegerPay also fires webhooks for async events (delayed clearing, refunds, disputes). Add a
/webhooks/shegerpayroute to your backend.
Total time, end to end: about five minutes.
Code samples
JavaScript / TypeScript
import ShegerPay from '@shegerpay/sdk';
const sp = new ShegerPay(process.env.SHEGERPAY_KEY);
const result = await sp.verify({
transactionId: 'AB12CD34EF',
provider: 'telebirr',
expectedAmount: 1500,
expectedAccount: '+251911234567',
});
if (result.verified) {
await unlockOrder(result.metadata.orderId);
} else {
await flagForReview(result.reason);
}
Python
from shegerpay import ShegerPay
sp = ShegerPay(api_key=os.environ["SHEGERPAY_KEY"])
result = sp.verify(
transaction_id="AB12CD34EF",
provider="telebirr",
expected_amount=1500,
expected_account="+251911234567",
)
if result.verified:
unlock_order(result.metadata["order_id"])
PHP
use ShegerPay\Client;
$sp = new Client(getenv('SHEGERPAY_KEY'));
$result = $sp->verify([
'transactionId' => 'AB12CD34EF',
'provider' => 'telebirr',
'expectedAmount' => 1500,
'expectedAccount' => '+251911234567',
]);
if ($result->verified) {
unlock_order($result->metadata['order_id']);
}
All three return a normalized VerificationResult object with verified, amount, currency, payerAccount, timestamp, rawProviderResponse, and a reason field populated when verification fails.
Handling failed verifications
A verify call fails for a finite set of reasons. Handle each explicitly:
not_found— the transaction ID doesn't exist on Telebirr. Likely a fake screenshot. Reject, log, and consider rate-limiting the user.amount_mismatch— the transaction exists but the amount doesn't matchexpectedAmount. Show the customer the difference; don't auto-unlock.recipient_mismatch— the money went to a different account. Reject — customer paid the wrong place.pending— the transaction is in flight. Don't reject; queue and re-poll, or wait for the webhook.expired— too old to verify reliably. Telebirr records are queryable for a bounded window.
The pattern in code:
switch (result.reason) {
case 'pending': return queueRetry(result);
case 'amount_mismatch': return askCustomerToPayDifference(result);
default: return rejectAndLog(result);
}
Webhook setup for async confirmation
For Telebirr transactions that clear after a delay (rare but real, especially during peak load), use webhooks instead of polling. Register your webhook URL in the dashboard. ShegerPay POSTs JSON like:
{
"event": "verification.completed",
"transactionId": "AB12CD34EF",
"provider": "telebirr",
"verified": true,
"amount": 1500,
"metadata": { "orderId": "ord_8821" },
"signature": "..."
}
Always verify the HMAC signature on every webhook using the webhook secret from the dashboard. ShegerPay retries failed webhooks with exponential backoff for 24 hours.
Telebirr-specific edge cases
- FT references vs Telebirr references. Telebirr issues alphanumeric refs like
AB12CD34EF. Bank-to-Telebirr or RTGS transfers may use FT-prefixed refs. ShegerPay normalizes both — pass whichever the customer gives you. - Business vs personal accounts. Telebirr Business and Telebirr Personal are different products. ShegerPay supports both, but make sure the
expectedAccountyou pass matches what you registered. Mixing them returnsrecipient_mismatch. - Self-transfers and reversals. Telebirr customers occasionally reverse a transfer within the cooling window. Listen for
verification.reversedwebhooks and roll back the order. - Daily and per-transaction limits. Telebirr enforces KYC tiers. Large transactions may be split across multiple references — group them by
metadata.orderIdon your side. - Phone-number formats. Telebirr returns
2519XXXXXXXX(no+) in some payloads and+2519XXXXXXXXin others. The SDK normalizes to E.164 — don't compare raw strings.
FAQ
How fast is Telebirr verification? Under two seconds for the synchronous call. Webhooks usually fire within five seconds of the underlying transaction clearing.
Is there a sandbox? Yes. Use a sk_test_... key and the dashboard's Simulate Transaction tool to push fake Telebirr events into your test webhooks.
What does Telebirr verification cost? It's included in every ShegerPay plan. Free tier covers 100 verifications; Startup at $9/month covers 2,000. See /pricing.
Do I need approval from Ethio Telecom? No. Because ShegerPay is non-custodial — the customer pays your existing Telebirr Business wallet directly — you operate under your existing Telebirr merchant relationship. There's no separate gateway to be approved by.
Can I verify old transactions? Yes, within Telebirr's record-retention window (currently around 90 days for full detail). Older transactions return expired.
How does this compare to using Chapa for Telebirr? Chapa is custodial — it receives the payment and settles to you later. ShegerPay leaves the money in your Telebirr wallet and just verifies it. Read the full comparison in ShegerPay vs Chapa or the architecture explainer in What is non-custodial payment verification. Full SDK reference at /docs.