Developers
Accept a payment with one API call
Plain HTTPS and JSON, or the library for your language. Nothing to approve before you write your first line: test keys are yours the moment you sign up.
Quickstart
Three steps to your first payment
- 1
Create a payment
POST /v1/paymentswith an amount and your own reference. Sabi prices it and returns acheckout_url. - 2
Send your customer to the checkout
Redirect to the link, open it in a window, or draw it in a frame on your own page. The customer picks a method and pays.
- 3
Act on the callback
When the payment settles, Sabi posts
collection.settledto your URL. Verify the signature, then fulfil the order.
curl "$SABI_URL/v1/payments" \
-H "Authorization: Bearer $SABI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": "1250.00",
"currency": "ETB",
"type": "payment",
"external_reference": "ORDER-10482",
"payer_name": "Hanna Bekele",
"narrative": "Yirgacheffe, 500 g x 2"
}'app/api/sabi/route.js
import { verifyCallback } from "sabi-payments";
// Sabi signs the timestamp and the exact bytes it sends, so the raw body is
// read first and nothing parses it before the signature has been checked.
export async function POST(request) {
const raw = await request.text();
let callback;
try {
callback = verifyCallback(process.env.SABI_CALLBACK_SECRET, request.headers, raw);
} catch {
// Unsigned, mis-signed, or older than five minutes: answer 401 and do nothing.
return new Response("unsigned", { status: 401 });
}
// A delivery can arrive twice. Record callback.deliveryId and ignore repeats.
await creditOrder(callback.reference, callback.deliveryId);
return new Response("ok");
}Libraries
Write less of this yourself
Sabi publishes a client for the two languages its own applications are built in, a script for the checkout, and a plugin for WooCommerce. All of them are MIT or GPL, and all of them speak the API documented here, so nothing below is closed to you if you would rather call it directly.
Node and TypeScript
Typed, dependency-free, and it verifies callbacks for you. Node 18.17 and up, as an ES module or CommonJS.
npm install sabi-paymentsGo
The standard library and nothing else, with the retries and the error kinds a payment needs. Go 1.22 and up.
go get github.com/YosefAshenafi/sabi-goThe checkout, on your page
One script opens the hosted checkout over your own page and tells you when it is paid. It falls back to the page itself where a browser will not frame it.
<script src="$SABI_URL/checkout/static/sabi.js"></script>WooCommerce
A plugin for the shop you already run: payments on the block checkout, and refunds from the order screen.
How it works
import { SabiClient } from "sabi-payments";
const sabi = new SabiClient({
baseURL: process.env.SABI_URL,
apiKey: process.env.SABI_API_KEY,
});
// Idempotent on your own reference: a repeat returns the payment it made.
const payment = await sabi.createPayment({
amount: "1250.00",
currency: "ETB",
external_reference: "ORDER-10482",
payer_name: "Hanna Bekele",
narrative: "Yirgacheffe, 500 g x 2",
});
redirect(payment.checkout_url);The same first payment, in each library. Both are idempotent on your own reference, so a request you repeat returns the payment it already made rather than a second one.
Built for money
The details that matter when the money is real
Signed, retried callbacks
HMAC-SHA256 over the timestamp and the exact body, in X-Sabi-Signature-V2. A callback captured on the wire is worthless five minutes later, and a delivery that fails is retried with exponential backoff.
Safe to retry
Payments, payouts and transfers are idempotent on your external_reference. A repeated request returns the transaction it already created.
Fees on every response
Each transaction carries its fee breakdown, frozen when it was priced. Render it; never compute a fee yourself.
Sandbox and live keys
Create tpk_sandbox_ keys in the dashboard as soon as you sign up. tpk_live_ keys start working once your business is verified.
One error shape
Conventional status codes, a stable error code to branch on, and a request_id on every answer.
Payouts and transfers by API
Send money to wallets and bank accounts, or to another Sabi business, with the same keys.
Sandbox
Test the failure path, not only the happy one
In the sandbox every method settles test money, and an account number ending in 0000 is declined at the checkout. The number still has to be the right shape for the method.
| Method | Declines with |
|---|---|
| telebirr | 0911220000 |
| CBE Birr | 0911220000 |
| M-PESA | 0711220000 |
| CBE account | 1000123450000 |
| Card | 4242424242420000 |
Reference
What the checkout accepts
| Method | Type | What the payer enters | Payouts |
|---|---|---|---|
| telebirr | Mobile wallet | A 10-digit mobile number starting 09 | Yes |
| CBE Birr | Mobile wallet | A 10-digit mobile number starting 09 or 07 | Yes |
| M-PESA | Mobile wallet | A 10-digit mobile number starting 07 | Yes |
| CBE account | Bank account | A 13-digit Commercial Bank of Ethiopia account number | Yes |
| Visa | Card | Any card number that passes the Luhn check | Collections only |
| Mastercard | Card | Any card number that passes the Luhn check | Collections only |

Get your test keys
Sign up with your phone number, open Manage Business, and create a sandbox key. No review needed for test mode.
