Payo is a receipt-based payment gateway for businesses whose customers pay by CBE, Telebirr, or Abyssinia transfer. The customer pays at their bank app, uploads the receipt, and your server gets a signed webhook. OCR auto-approves the clean ones. The rest are reviewed by a human on your team.
Order #1042 · Demo Store
250.00 ETB
expires in 09:22
Step 1 · Pay to this account
CBE
Commercial Bank of Ethiopia
1000 1234 5678 9012
Account name · Demo Store Trading PLC
Step 2 · Upload the receipt screenshot
Successful transfer
Ref: PY-1042-KJ3
Screenshot from CBE Mobile · 487 KB
Waiting for the Demo Store team to approve
Usually under 5 minutes during working hours.
Order approved · 12.4s after upload
Approved by simon@ · webhook delivered to demo-store.dev
Live feed
Recent payments
ord_8h2k9f · Demo Store
M. Bekele — 250.00 ETB
Ref PY-1042-KJ3 · verified 12s after upload
The customer sees the left screen. Your team sees the right one. When the order is approved, both turn green within seconds, and your backend gets the webhook.
Works with the banks your customers already use

CBE
CBE

Telebirr
Telebirr

Abyssinia
Abyssinia Bank
Plus Dashen, Awash, Zemen, CBE Birr, and M-Pesa. Logos belong to their respective owners. We just send HTTP to them.
01 / Verification
Every receipt comes in through the same door, then splits. The OCR lane reads the amount, the reference, and the sending account, and approves anything that matches the order. Anything it can't confirm (wrong amount, missing reference, blurry image) goes to the human lane with the OCR output and the original side by side.
Your team only sees the receipts that need a judgement. The rest are already done by the time they've had their coffee.
Lane 1
OCR for every receipt. Reads the amount, the reference, and the sending account, and approves the ones that match the order.
Lane 2
Your team for the receipts OCR can't confirm. Approves, rejects, or messages the customer back with a reason.
We never reject a receipt automatically. If the OCR can't confirm it, the receipt degrades to human review. It does not push back to the customer. We'd rather have a human look at it than have a bot tell a paying customer their money didn't land.
02 / What's in the box
We started Payo because the receipt-verification loop is the same four steps every time, and we kept rebuilding it. So we built it once, properly: idempotent, signed webhooks, a queue that doesn't drop payments. Then we put it behind a single REST endpoint.
No card details, no redirect, no OTP screen. They open CBE, Telebirr, or Abyssinia, transfer the amount, and come back to upload the receipt.
Our OCR reads the receipt, checks the amount and reference against your order, and approves the obvious cases in seconds. No human needed.
Anything the OCR can't confirm (wrong reference, cropped screenshot, suspicious amount) goes to your team, with everything they need to make the call.
One POST to your server, payload signed with HMAC-SHA256. We retry with backoff. You verify the header and treat the order as paid.
POST /api/v1/orders with amount and description. We hand you a payment URL. No SDK, no proprietary client library.
After 10 minutes an unpaid order is cancelled and your customer sees a clear "this link expired" page. No orphaned orders sitting in PENDING.
03 / How it works
Sign up, create a project, paste in the bank accounts you accept. We give you an API key.
From your app, server, or no-code flow: POST /api/v1/orders with amount, description, and callbackUrl. Idempotent.
The customer pays and uploads the receipt. Our OCR auto-approves the clean ones. The rest go to your team. The webhook fires either way.
04 / The API
The request shape is the smallest thing we could keep stable. The webhook payload is the source of truth. Everything else is a convenience.
x-pygate-signature before trusting the payload.curl -X POST https://api.pygate.et/v1/orders \
-H "x-api-key: pgk_abc123…" \
-H "content-type: application/json" \
-d '{
"amount": 250,
"description": "Order #1042",
"callbackUrl": "https://you.dev/hook"
}'x-pygate-signature: sha256=a3f9c1…
{
"orderId": "ord_8h2k9f",
"status": "APPROVED",
"amount": 250,
"currency": "ETB"
}05 / Who's using it
You invoice a customer monthly. Email them a Payo URL with the amount prefilled. They pay, you get the webhook, you mark the invoice paid.
5 ETB–25,000 ETB recurring
Wire Payo as a bank-transfer option alongside Chapa or Telebirr USSD. Customers who pay by transfer get a working link, not a checkout aisle you have to babysit.
Variable cart totals
You're a one-person shop. Send a unique payment link per client. Stop sharing your phone number over Telegram and waiting for screenshots.
One-off, ad-hoc amounts
06 / Pricing
The money moves straight from your customer to your bank account, so no third party takes a cut. Everything on this page — the API, the OCR auto-verification, the human review queue, the signed webhooks — is free, with no caps. We will release advanced features in the future, and those will be paid. The service you're using today stays free.
Everything you need to accept bank transfers today. No setup fees, no monthly fees, no per-transaction fees, no caps. The service you're using is free, period.
We will release advanced features in the future, and those will be paid. The current service stays free whatever you do — you'd only ever pay for new capabilities, priced and announced before they exist.
07 / FAQ
Every receipt goes through OCR first. We read the amount, the reference, the sending account, and the timestamp. If they match the order, the receipt is approved in a few seconds and no human is involved. If anything is off (wrong amount, missing reference, blurry image, payment from an unknown account), we route it to a human with the OCR output and the original image side by side. Your team only sees the receipts that need a judgement call.
We send it to the human queue with a note about what we couldn't extract. The customer sees a clear "we're reviewing your payment" state. We never reject a receipt automatically. A bot should not tell a paying customer their payment failed.
Chapa and SantimPay sit on card and USSD rails. Most Ethiopian customers can't reach those rails. Payo sits on the rail that works: bank transfer. You can run Payo alongside Chapa on the same checkout. They solve different problems.
A customer submitting a receipt they didn't pay for: an edited screenshot, the wrong reference, or the wrong amount. OCR plus human review catches it. Fraudulent receipts go to a senior reviewer and the customer's account is flagged for future orders. We log every escalation with the reason, so you can audit it.
An admin rejects it with a reason. The customer gets a clear page explaining why, with a button to upload a new receipt. The order stays PENDING until either the 10-minute timer expires or a clean receipt is approved.
Every webhook payload is signed with HMAC-SHA256 using your project API key. The signature is in the x-pygate-signature header. Verify it on your server before trusting the payload. The docs walk through it in five languages.
No. Sign up, get an API key, start accepting payments the same hour. No business license and no approval queue.
Yes. Once your business is verified, payment links can be served from your own subdomain (pay.yourbrand.com, for example). We handle the certificate.
We want to see what the receipt verification loop looks like at scale, and we want to find the merchants who are already doing this themselves and stitch it together for them. So the current service is free: no setup fees, no monthly fees, no per-transaction fees. We will release advanced features in the future, and those will be paid. The service you're using stays free. We won't know what we're actually selling until we see real volume.
Anything else? hello@payo.et, or the in-app chat. We read both.