Skip to content

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. 1

    Create a payment

    POST /v1/payments with an amount and your own reference. Sabi prices it and returns a checkout_url.

  2. 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. 3

    Act on the callback

    When the payment settles, Sabi posts collection.settled to 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-payments
  • Go

    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-go
  • The 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.

Accounts that decline in the sandbox
MethodDeclines with
telebirr0911220000
CBE Birr0911220000
M-PESA0711220000
CBE account1000123450000
Card4242424242420000

Reference

What the checkout accepts

Payment methods and the account each one takes
MethodTypeWhat the payer entersPayouts
telebirrMobile walletA 10-digit mobile number starting 09 Yes
CBE BirrMobile walletA 10-digit mobile number starting 09 or 07 Yes
M-PESAMobile walletA 10-digit mobile number starting 07 Yes
CBE accountBank accountA 13-digit Commercial Bank of Ethiopia account number Yes
VisaCardAny card number that passes the Luhn checkCollections only
MastercardCardAny card number that passes the Luhn checkCollections 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.