Skip to main content

Account Balance

Request account balances, receive asynchronous result notifications, and handle timeout callbacks from Safaricom's Account Balance API.

User Story

Who can use this service

As a finance or operations user, I want to query my organization's M-Pesa account balance so I can reconcile payments and spot problems quickly.

  • Finance analyst: runs balance checks and reconciliation reports.
  • Till manager / cashier: monitors till balances and requests top-ups when low.
  • Backend system / automation: polls balances regularly and creates alerts or workflows on anomalies.

Parameters Definition

ParameterTypeDescription
Initiatorrequired
str
StringUsername used to initiate the request (API initiator identity).
SecurityCredentialrequired
str
StringEncrypted security credential (as required by Safaricom).
CommandIDrequired
str
StringTransaction type. Default: 'AccountBalance'.
PartyArequired
int
IntegerOrganization shortcode, till number or MSISDN depending on IdentifierType.
IdentifierTyperequired
int
IntegerType of PartyA. Allowed values: 1 (MSISDN), 2 (Till number), 4 (Short code).
Remarksrequired
str
StringFreeform comments for the transaction (must not exceed 100 characters).
QueueTimeOutURLrequired
str
StringCallback URL for timeout notifications (QueueTimeOutURL).
ResultURLrequired
str
StringCallback URL where final account balance result will be posted.

Responses & Result Notifications

ParameterTypeDescription
OriginatorConversationID
str
StringUnique identifier for the original request acknowledgement.
ConversationID
str
StringUnique transaction identifier returned by M-Pesa.
ResponseCoderequired
str|int
String/IntegerAcknowledgement status code. '0' indicates acceptance of the request.
ResponseDescriptionrequired
str
StringHuman readable acknowledgement message.
Result.ResultType / Result.ResultCode / Result.ResultDescrequired
int|int|str
Integer/Integer/StringResult metadata posted to ResultURL. ResultType=0 => success; ResultCode=0 => success.
Result.ResultParameter.ResultParameters
list
ArrayList of key/value result parameters. 'AccountBalance' key contains pipe- and ampersand-delimited balance blobs for accounts.
ReferenceData.ReferenceItem
object
ObjectReference item included in callbacks (e.g., original QueueTimeoutURL).

Overview

Query lifecycle: send AccountBalance request → receive immediate ack → process asynchronous result or timeout.

Quick Setup

Python (example)
# Requires: mpesakit, fastapi (for webhook example)
from mpesakit import MpesaClient
from mpesakit.account_balance import (
AccountBalanceResultCallback,
AccountBalanceTimeoutCallback,
AccountBalanceIdentifierType,
)
from mpesakit.security.ip_whitelist import is_mpesa_ip_allowed
# ---- Client / Request (synchronous example) ----
client = MpesaClient(consumer_key="YOUR_KEY", consumer_secret="YOUR_SECRET", environment="sandbox")
resp = client.balance.query(
initiator="apiuser",
security_credential="ENCRYPTED_SECURITY_CREDENTIAL",
party_a=600000,
identifier_type=AccountBalanceIdentifierType.SHORT_CODE,
remarks="Balance inquiry",
result_url="https://your.service/webhook/account-balance/result",
queue_timeout_url="https://your.service/webhook/account-balance/timeout",
)
if resp.is_successful():
print("Acknowledgement accepted:", resp.ConversationID or resp.OriginatorConversationID)
else:
print("Acknowledgement failed:", resp.ResponseDescription)

Webhook Handling (Result & Timeout)

Webhook Example (FastAPI)
# Example: FastAPI endpoints for Account Balance Result & Timeout callbacks
from fastapi import FastAPI, Request, HTTPException
from mpesakit.account_balance import (
AccountBalanceResultCallback,
AccountBalanceTimeoutCallback,
AccountBalanceResultCallbackResponse,
AccountBalanceTimeoutCallbackResponse,
)
from mpesakit.security.ip_whitelist import is_mpesa_ip_allowed
import logging
app = FastAPI()
logger = logging.getLogger("mpesa.account_balance")
async def _caller_ip(request: Request) -> str:
return (request.headers.get("x-forwarded-for") or request.client.host).split(",")[0].strip()
@app.post("/webhook/account-balance/result")
async def account_balance_result(request: Request):
payload = await request.json()
caller_ip = await _caller_ip(request)
if not is_mpesa_ip_allowed(caller_ip):
raise HTTPException(status_code=403, detail="forbidden")
# Validate and parse incoming payload into project models
try:
callback = AccountBalanceResultCallback(**payload)
except Exception as exc:
logger.exception("Invalid AccountBalance result payload")
return AccountBalanceResultCallbackResponse(ResultCode=1, ResultDesc=f"Invalid payload: {exc}")
response = callback.model_dump(mode="json")
logger.info("AccountBalance result received: %s", response)
return AccountBalanceResultCallbackResponse() # defaults to ResultCode=0, ResultDesc="Result received and processed successfully."
@app.post("/webhook/account-balance/timeout")
async def account_balance_timeout(request: Request):
payload = await request.json()
caller_ip = await _caller_ip(request)
if not is_mpesa_ip_allowed(caller_ip):
raise HTTPException(status_code=403, detail="forbidden")
try:
callback = AccountBalanceTimeoutCallback(**payload)
except Exception as exc:
logger.exception("Invalid AccountBalance timeout payload")
return AccountBalanceTimeoutCallbackResponse(ResultCode=1, ResultDesc=f"Invalid payload: {exc}")
response = callback.model_dump(mode="json")
logger.warning("AccountBalance timeout received: %s", response)
# Quick typed acknowledgement to stop retries
return AccountBalanceTimeoutCallbackResponse() # defaults to ResultCode=0, ResultDesc="Timeout notification received and processed successfully."

Important behaviors & validations

Response helpers

  • A returned AccountBalanceResponse model exposes:
  • is_successful() helper that treats any all-zero ResponseCode string (e.g., "0" or "000") as success.
  • Result callbacks are parsed into AccountBalanceResultCallback with:
  • ResultParameter.ResultParameters list containing an 'AccountBalance' entry holding a delimited balance string.

Testing & Expected Behaviors

  • Query:

    • Should return an AccountBalanceResponse acknowledgement. Confirm is_successful() for ResponseCode == 0.
    • HTTP client must receive the correct path and headers (Authorization Bearer token + JSON content-type).
  • Result Callback:

    • Parse Result.ResultParameter.ResultParameters and extract the AccountBalance key. Persist parsed balances for reconciliation.
    • Ensure your handler returns the expected acknowledgement payload quickly so provider retries are avoided.
  • Timeout Callback:

    • QueueTimeOutURL receives a Result with ResultType=1; handle accordingly (mark request as timed out, alert operations).

Next Steps