JamiDev

Withdrawals

Pay out your organization's earnings to Telebirr, M-Pesa or CBE Birr from code, and track each payout until it lands.

A withdrawal moves your organization's earnings to a phone wallet. This page is the end-to-end guide: what you need, how much arrives, how to send one safely, and how to know it landed. For every field and status code, see API: Withdrawals.

Before you start

Withdrawals from the API need all of:

  • a production organization and a jamidev_live_… token (sandbox earnings are simulated and can't be withdrawn);
  • Developer Mode on for the organization's owner;
  • approved KYC for the owner.

If one is missing, createWithdrawal returns 400 with a message naming it.

How much arrives

The JamiDev fee and any government tax are cut from the amount you withdraw, not added on top. You withdraw amount; the account receives netAmount.

You withdrawFee (7%)TaxArrives
100.00 ETB7.00 ETB0.00 ETB93.00 ETB
5,000.00 ETB350.00 ETB0.00 ETB4,650.00 ETB
20,000.00 ETB1,400.00 ETB0.00 ETB18,600.00 ETB

The table uses the default rates. Jami can set a different fee on your account, and the tax rate is a Jami platform setting. The server applies the real rates and returns them on every withdrawal as feeAmount, taxAmount and netAmount. Each part is rounded to the nearest santim.

Preview offline with the SDK. This makes no request:

import { Jami } from 'jami-sdk';

Jami.computeWithdrawalQuote(500000);
// { amount: 500000, feeAmount: 35000, taxAmount: 0, netAmount: 465000, feeRate: 0.07, taxRate: 0 }

Limits

LimitValue
Minimum per withdrawal100.00 ETB (10000 santim)
Paid out automaticallyUp to 10,000.00 ETB. Larger amounts wait for manual review.
Per organization, rolling 24 hours100,000.00 ETB. Past it, 429.
Maximum per withdrawalYour organization's withdrawable earnings

Send a withdrawal

Check what you can withdraw

const jami = new Jami({ token: process.env.JAMI_TOKEN! }); // jamidev_live_…

const { balanceMinor, eligible } = await jami.getBalance();

balanceMinor is what this organization has earned and not yet withdrawn. eligible is false, with a balance of 0, when the organization isn't in production.

Create it with an idempotency key

const withdrawal = await jami.createWithdrawal({
  amount: 500000,                        // 5,000.00 ETB, gross
  gateway: 'telebirr',                   // 'telebirr' | 'mpesa' | 'cbe'
  account: '251911223344',               // destination phone
  idempotencyKey: `payout-${payoutId}`,  // one key per payout you intend to make
});

withdrawal.status;    // 'processing' or 'completed' (≤ 10,000 ETB), else 'pending'
withdrawal.netAmount; // 465000

Generate the key once for each payout you mean to make and store it with your own record. If the request times out, call again with the same key: you get the original withdrawal back (200 instead of 201) and nothing is paid twice.

Wait for the result

Listen for the withdrawal.paid and withdrawal.failed webhooks, or poll:

const latest = await jami.getWithdrawal(withdrawal._id);

Statuses

StatusMeaning
pendingOver 10,000 ETB; waiting for manual review.
reviewingUnder review.
approvedApproved; waiting to be paid out.
processingThe payout is being sent.
completedPaid. netAmount reached the account. withdrawal.paid is sent.
rejectedRejected, or the payout failed. The amount returns to your balance. withdrawal.failed is sent.

A payout that fails at the gateway can go back to approved for a retry, which also sends withdrawal.failed. If the retry succeeds you'll then get withdrawal.paid.

Errors

StatusWhenWhat to do
400Bad amount or gateway, invalid phone, not a production organization, Developer Mode off, KYC not approved, or more than the organization's earningsRead the message; fix the input or the account setup.
402Your earnings cover it, but the owner's Jami wallet doesn't right now, for example because money was spent elsewhere or another payout is still on holdWithdraw less, or wait for the pending payout to settle.
429The 100,000 ETB rolling-24h cap for this organizationWait, or withdraw less.

Why 402 can happen after a healthy balance

Earnings from JamiDev, tips and your shop all sit in one Jami wallet. getBalance reports what this organization earned, which caps what its token may withdraw. The money itself leaves the shared wallet, so if it was drawn down elsewhere, a withdrawal within the organization's earnings can still fail with 402.

In the SDK, 400 raises JamiValidationError, 429 raises JamiRateLimitError, and 402 raises JamiError with status: 402.

Next

On this page