How to Verify CBE and Telebirr Transactions in Real Time

Quick Answer: To verify a CBE or Telebirr transaction in real time, call ShegerPay's verify endpoint with the transaction reference (e.g. FT26... for CBE). The SDK queries the bank or wallet, confirms the amount and recipient match, and returns a verified status in under a second. Configure a webhook to handle async confirmations and retries.

Ethiopian merchants have lived with the same verification problem for years: a customer says they sent money, screenshots an SMS, and you have to decide whether to ship the goods, unlock the account, or wait. Manual verification — opening the CBE app, scrolling for the FT reference, eyeballing the amount — does not scale past a few orders a day, and it leaks revenue both ways. You either trust too easily and eat fraud, or trust too slowly and lose impatient customers. Real-time verification flips that trade-off: every transfer is confirmed by API the moment it clears, and your application can react instantly. This guide walks through exactly how to set it up with the ShegerPay SDK, in three languages, with webhook fallback for the long-tail cases.

Why manual verification fails

Manual verification fails for four compounding reasons. First, it doesn't scale linearly — checking five transfers takes five minutes, checking fifty takes an hour, and checking five hundred is impossible without a team. Second, it's error-prone. Humans transpose digits, miss decimal points, and confuse similar reference numbers. Third, it's slow. Even a fast operator takes 30–60 seconds per check, and customers feel that delay as friction. Fourth, it's unauditable — there's no immutable trail of who verified what when, which becomes a problem when disputes arise.

The hidden cost is worst of all: every manually verified order has a verification queue. Customers wait. Some abandon. Some message support angrily. Some try to pay twice. Your conversion rate quietly bleeds for reasons your analytics don't capture. Real-time API verification removes the queue entirely. The same staff member who used to verify transfers can now do something that actually grows the business.

Screenshots and SMS-based confirmation also create a fraud surface. A photoshopped SMS or a forwarded transfer notification looks identical to a real one until you check the bank record. Most Ethiopian merchants have at least one story about a fake screenshot. Programmatic verification eliminates that surface — the only source of truth is the bank itself.

What instant verification means

Instant verification means your application asks the bank or wallet directly, in real time, whether a specific reference number cleared into your account for the expected amount. The answer is a structured JSON response with a status (verified, pending, failed), the matched transaction record, and a cryptographic confirmation.

"Instant" in this context is sub-second for ShegerPay's verification API. From the customer's perspective, the experience is: they hit "I've paid" in checkout, see a brief spinner, and the next screen confirms success. There's no human in the loop. There's no waiting for a batch settlement file. There's no email back-and-forth with the customer.

This is the same model that Stripe, PayPal, and other global payment platforms have used for a decade in markets where card rails dominate — Ethiopia just needed an API layer over the local rails to get the same UX.

Setting up ShegerPay verification

Sign up at shegerpay.com, grab your API key from the dashboard, and install the SDK for your stack. The verify call is the same shape across all 12 SDKs.

JavaScript / TypeScript

import { ShegerPay } from "@shegerpay/sdk";

const sp = new ShegerPay({ apiKey: process.env.SHEGERPAY_API_KEY });

const result = await sp.verify({
  transactionId: "FT26123456789",
  rail: "cbe",
  expectedAmount: 1500,
});

if (result.status === "verified") {
  await fulfillOrder(result.orderId);
}

Python

from shegerpay import ShegerPay

sp = ShegerPay(api_key=os.environ["SHEGERPAY_API_KEY"])

result = sp.verify(
    transaction_id="FT26123456789",
    rail="cbe",
    expected_amount=1500,
)

if result.status == "verified":
    fulfill_order(result.order_id)

PHP

use ShegerPay\Client;

$sp = new Client(getenv('SHEGERPAY_API_KEY'));

$result = $sp->verify([
    'transactionId' => 'FT26123456789',
    'rail' => 'cbe',
    'expectedAmount' => 1500,
]);

if ($result->status === 'verified') {
    fulfillOrder($result->orderId);
}

For Telebirr, swap rail: "cbe" for rail: "telebirr" and pass the Telebirr transaction reference. The same pattern works for Awash, Dashen, BOA, Ebirr, USDT, PayPal, and Wise — only the rail string changes. See /docs for the full rail enum.

Handling failed verifications

A verification can fail for legitimate reasons: the reference doesn't exist yet (transfer still in flight), the amount mismatches (customer underpaid or overpaid), the recipient account doesn't match (paid to the wrong number), or the transfer was reversed.

Handle each case explicitly. For pending, surface a "we're confirming your payment" state to the customer and rely on the webhook (next section) for the final confirmation. For amount_mismatch, hold the order and notify the customer with the exact discrepancy. For not_found after a reasonable retry window, mark the order unpaid and let the customer try again. For reversed, treat it as a refund and unwind any partial fulfillment.

Never silently retry forever. The SDK supports an attempts parameter and exponential backoff, but cap your retries at a sane window (60 seconds for synchronous flows, several minutes for batch reconciliation) and fall back to async webhooks beyond that.

Webhook setup for async confirmation

For the long-tail cases where a transfer takes longer than your synchronous verify window — slow banks, off-hour traffic, network blips — webhooks are how you stay correct without making customers wait. Register a webhook URL in the ShegerPay dashboard pointing at an endpoint on your server, and ShegerPay will POST signed events when verification eventually completes.

Example Node webhook handler:

import express from "express";
import { ShegerPay } from "@shegerpay/sdk";

const app = express();
const sp = new ShegerPay({ apiKey: process.env.SHEGERPAY_API_KEY });

app.post("/webhooks/shegerpay", express.raw({ type: "*/*" }), (req, res) => {
  const event = sp.webhooks.verify(req.body, req.headers["x-shegerpay-signature"]);
  if (event.type === "payment.verified") {
    fulfillOrder(event.data.orderId);
  }
  res.sendStatus(200);
});

Always verify the HMAC signature with the SDK before trusting the payload — never act on raw webhook content. Respond with a 2xx within a few seconds; ShegerPay retries non-2xx responses with exponential backoff. Make your handler idempotent — the same event may arrive twice, and processing it twice should not double-fulfill.

Common pitfalls

Five pitfalls catch nearly every team integrating real-time verification for the first time.

Verifying without amount check. Always pass expectedAmount. Without it, a customer can transfer 1 birr and your code will mark the order paid. The amount check is the cheapest fraud defense you'll ever ship.

Trusting the client-supplied reference. The customer pastes the FT reference into your form. They can paste a reference from a totally unrelated transfer they did last month. The verify call catches this — but only if you also check that the transaction's timestamp falls inside your order window.

No idempotency on fulfillment. If the same transactionId resolves twice (once via synchronous verify, once via webhook), and your fulfillment isn't idempotent, you'll deliver twice. Key your fulfillment off the order ID, not the verification event.

Storing API keys in client code. The verify call must happen from your server. A leaked API key lets anyone verify on your account and rack up bill or exfiltrate data. Use environment variables and a server-side endpoint.

Skipping webhook signature verification. Anyone who finds your webhook URL can POST fake events. Always run sp.webhooks.verify(...) before acting on a payload.

Avoid these and the rest of the integration is straightforward. See /pricing for plan details and our comparison post for how this approach differs from custodial gateways.

FAQ

Q: How fast is ShegerPay's verification? Typically sub-second for CBE and Telebirr. International rails (PayPal, Wise) and crypto (USDT) depend on the underlying network and may take a few seconds.

Q: Can I verify Telebirr transactions without the customer's permission? You can verify transfers into accounts you own. ShegerPay queries your accounts using credentials you provide at onboarding — it does not access customer accounts.

Q: What if the FT reference is wrong? The verify call returns not_found. Show the customer a friendly error and let them re-enter or re-paste the reference.

Q: Do I need a webhook if I verify synchronously? Yes, for resilience. Synchronous verify handles 95%+ of cases; webhooks catch the rest (slow bank responses, retries, edge cases) without making customers wait on a long-polling spinner.

Q: Can I verify historical transactions? Yes — verify works on any reference visible in your account, subject to the bank's own history retention.

Q: How do I get help integrating? Email [email protected], call +251 998 169 242, or message @shegerpay_0 on Telegram. SDK docs are at /docs.