Skip to main content
Yonne deducts the delivery_fee from your merchant wallet when each order is created. If your balance drops below the fee for a delivery, the create-order call fails with 402 Insufficient Funds. This guide shows you how to check balance, respond to that error, and build a proactive top-up workflow.

Check your wallet balance

Call GET /api/v1/external/wallet to get your current balance:
curl --request GET "https://api.yonne.app/api/v1/external/wallet" \
  --header "X-API-Key: yonne_live_xxxxxxxxx"
Response:
{
  "success": true,
  "balance": 12500,
  "currency": "MWK",
  "environment": "live"
}

Handling a 402 Insufficient Funds error

When create-order returns 402, the response tells you exactly what you have and what you need:
{
  "success": false,
  "error": "Insufficient Funds",
  "message": "Wallet balance is insufficient for this delivery. Please top up.",
  "balance": 1500,
  "required": 7350
}
Your response should be:
  1. Don’t fail silently. The customer’s payment succeeded — their order must not simply disappear.
  2. Flag the order in your system as dispatch_pending_funds.
  3. Alert your operations team with the shortfall: required - balance = 5850 MWK needed.
  4. Tell the customer that their order is confirmed and the delivery will be dispatched shortly.
  5. After topping up, retry create-order with the same Idempotency-Key to dispatch.

The full 402 recovery flow

async function dispatchWithFundsCheck(internalOrder) {
  const idempotencyKey = `order-${internalOrder.id}-attempt-1`;

  const res = await fetch("https://api.yonne.app/api/v1/external/create-order", {
    method: "POST",
    headers: {
      "X-API-Key": process.env.YONNE_API_KEY,
      "Content-Type": "application/json",
      "Idempotency-Key": idempotencyKey
    },
    body: JSON.stringify({
      delivery_address: internalOrder.deliveryAddress,
      receiver_name: internalOrder.customerName,
      receiver_phone: internalOrder.customerPhone,
      delivery_fee: internalOrder.deliveryFee,
      delivery_lat: internalOrder.deliveryLat,
      delivery_lng: internalOrder.deliveryLng,
      item_name: internalOrder.itemName,
      product_type: "Package",
      weight_estimate: internalOrder.weightEstimate,
      merchant_reference_id: internalOrder.id
    })
  });

  const data = await res.json();

  if (res.status === 402) {
    // Flag for ops team
    await db.orders.update(internalOrder.id, { status: "dispatch_pending_funds" });
    await alertOpsTeam({
      type: "insufficient_funds",
      orderId: internalOrder.id,
      balance: data.balance,
      required: data.required,
      shortfall: data.required - data.balance,
      idempotencyKey // save so ops can retry with the same key
    });
    return { dispatched: false, reason: "insufficient_funds" };
  }

  if (!data.success) throw data;

  await db.orders.update(internalOrder.id, {
    status: "dispatched",
    yonne_order_id: data.order_id,
    yonne_tracking_id: data.tracking_id
  });
  return { dispatched: true, ...data };
}

Retrying after a top-up

Once your ops team tops up the wallet, retry the original create-order call using the exact same Idempotency-Key you used on the first attempt. Yonne will see the key, check that the original order was not yet created, and process it now.
Node.js
// Ops retries from the saved idempotency key
await retryDispatch({
  orderId: "WEB-100245",
  idempotencyKey: "order-WEB-100245-attempt-1"
});
Do not generate a new key — that would create a duplicate order.

Proactive balance monitoring

To avoid 402 errors reaching customers at all, set up a daily or hourly balance check and alert your team when balance drops below a safe threshold:
async function checkWalletHealth(minBalanceMWK = 50000) {
  const res = await fetch("https://api.yonne.app/api/v1/external/wallet", {
    headers: { "X-API-Key": process.env.YONNE_API_KEY }
  });
  const { balance, currency } = await res.json();

  if (balance < minBalanceMWK) {
    await alertOpsTeam({
      type: "low_wallet_balance",
      balance,
      currency,
      threshold: minBalanceMWK
    });
  }
}
Run this on a schedule (e.g. every hour via cron) so your ops team knows to top up before orders start failing.

Top-up process

Wallet top-ups are done through the Yonne merchant dashboard. The API does not expose a direct top-up endpoint — this is intentional to keep financial transactions in a controlled, audited channel. After topping up in the dashboard, confirm the new balance before retrying pending dispatches:
curl --request GET "https://api.yonne.app/api/v1/external/wallet" \
  --header "X-API-Key: yonne_live_xxxxxxxxx"