Tax Remittance
Remit taxes to KRA using M-Pesa by initiating tax payment requests and handling asynchronous result notifications.
User Stories
- As a fintech product owner, I want to programmatically remit taxes to KRA so that I can automate compliance processes.
- As an integrations developer, I want a simple client and clear webhook callbacks so I can implement reliable end-to-end flows with minimal boilerplate.
- As a billing operations engineer, I want result and timeout notifications with acknowledgements so I can reconcile transactions and trigger retries or alerts when needed.
- As a reseller partner, I want a tested SDK and examples so I can onboard quickly and reduce integration defects.
Parameters Definition
Parameter | Type | Description |
---|---|---|
Initiatorrequired str | String | Username used to initiate the request (must be pre-approved by Safaricom). |
SecurityCredentialrequired str | String | Encrypted security credential of the initiator (base64 encoded). |
Amountrequired int | Integer | Transaction amount in KES to be remitted to KRA. |
PartyArequired int | Integer | Shortcode from which money is deducted for tax remittance. |
AccountReferencerequired str | String | Payment Registration Number (PRN) issued by KRA for the tax payment. |
Remarksrequired str | String | Additional information for the transaction (maximum 100 characters). |
QueueTimeOutURLrequired str | String | HTTPS endpoint that will receive timeout notifications. |
ResultURLrequired str | String | HTTPS endpoint that will receive result notifications. |
PartyB int | Integer | KRA shortcode (default: 572572). |
CommandID str | String | Command ID for the transaction (default: 'PayTaxToKRA'). |
SenderIdentifierType int | Integer | Identifier type for sender (default: 4 for shortcode). |
RecieverIdentifierType int | Integer | Identifier type for receiver (default: 4 for shortcode). |
OriginatorConversationID str | String | Unique ID for the request message (returned in response). |
ConversationID str | String | Unique ID for the transaction (returned in response). |
ResponseCode str|int | String/Integer | Status code of the request (0 means success, returned in response). |
ResponseDescription str | String | Status message describing the request outcome (returned in response). |
Overview
Tax Remittance enables businesses to pay taxes directly to KRA through M-Pesa. It's an asynchronous operation with callbacks for results and timeouts.
💡Implementation Options
- Use the
MpesaClient
facade for simple and safe integration: it manages authentication, header injection and returns typed Pydantic models. - Use the Direct API (
TaxRemittance
service +TokenManager
+HttpClient
) if you need full control over request/response handling, middleware, or custom error behaviors.
💡Why use MpesaClient
The facade handles token retrieval and attaches Authorization headers so you can call high-level operations like initiating tax remittance with minimal boilerplate.
Quick Setup
Python
# Example: initiate tax remittance using the high-level clientfrom mpesakit import MpesaClient
client = MpesaClient(consumer_key="...", consumer_secret="...", environment="sandbox")
resp = client.tax.remittance( initiator="TaxPayer", security_credential="encrypted_credential", amount=239, party_a=888880, account_reference="353353", remarks="Tax payment for Q1", result_url="https://your.example/result", queue_timeout_url="https://your.example/timeout")
if resp.is_successful(): print("Tax remittance initiated successfully")else: print("Tax remittance failed:", resp.ResponseDescription)
💡Notes
- The facade returns typed Pydantic models (e.g., TaxRemittanceResponse) for ergonomic access to fields and helpers like is_successful().
- Authentication tokens are handled transparently by the client.
Webhook Handling (Result & Timeout)
Python
# Example: simple FastAPI endpoints for Tax Remittance Result and Timeoutfrom fastapi import FastAPI, Request, HTTPExceptionfrom mpesakit.tax_remittance import TaxRemittanceResultCallback, TaxRemittanceResultCallbackResponse, TaxRemittanceTimeoutCallback, TaxRemittanceTimeoutCallbackResponsefrom mpesakit.security.ip_whitelist import is_mpesa_ip_allowed
app = FastAPI()
@app.post("/tax/result")async def tax_result(request: Request): payload = await request.json() caller_ip = (request.headers.get("x-forwarded-for") or request.client.host).split(",")[0].strip() if not is_mpesa_ip_allowed(caller_ip): raise HTTPException(status_code=403, detail="forbidden")
data = TaxRemittanceResultCallback(**payload) # will validate incoming fields # process the result (update database, notify user, etc.) ack = TaxRemittanceResultCallbackResponse() return ack.model_dump(mode="json")
@app.post("/tax/timeout")async def tax_timeout(request: Request): payload = await request.json() caller_ip = (request.headers.get("x-forwarded-for") or request.client.host).split(",")[0].strip() if not is_mpesa_ip_allowed(caller_ip): raise HTTPException(status_code=403, detail="forbidden")
# process timeout notification (log, retry logic, etc.) ack = TaxRemittanceTimeoutCallbackResponse() return ack.model_dump(mode="json")
💡Important behavior
- Result and timeout endpoints should return acknowledgements (ResultCode 0). This confirms receipt to Mpesa Daraja API.
- Process the actual tax remittance result asynchronously based on the data in the result callback.
Responses & Helpers
Example Tax Remittance Success Response
{ "OriginatorConversationID": "5118-111210482-1", "ConversationID": "AG_20230420_2010759fd5662ef6d054", "ResponseCode": "0", "ResponseDescription": "Accept the service request successfully."}
💡Response Handling
- TaxRemittanceResponse provides is_successful() which treats any all-zero string (e.g., "0" or "00000000") as success.
- The SDK normalizes minor provider response typos (e.g., 'OriginatorCoversationID') so fields are accessible reliably.
Error Handling
Python
# Handle errors when calling the servicetry: resp = client.tax.remittance(...)except Exception as exc: # The underlying HTTP client may raise exceptions on network errors; log and retry as appropriate print("Tax remittance failed:", exc)
💡Notes
- HTTP or network errors raised by the HttpClient bubble up; wrap calls in try/except for robust production behavior.
- Tests exercise error flows to ensure exceptions propagate when the HTTP client fails.
Testing & Expected Behaviors
-
Tax Remittance:
- The service posts to
/mpesa/b2bpayment/v1/remittax
withAuthorization
header set viaTokenManager
. - Responses are returned as
TaxRemittanceResponse
instances. - The implementation tolerates a common provider typo ("OriginatorCoversationID") and maps it to
OriginatorConversationID
.
- The service posts to
-
Validation:
- Incoming payloads are validated against
TaxRemittanceResultCallback
andTaxRemittanceTimeoutCallback
. Missing required fields or invalid formats will raise validation errors. - Use the provided response schemas for acknowledgements; invalid codes are rejected by the model validator.
- Incoming payloads are validated against
Next Steps
💡What's Next?
- Implement robust webhook handlers for result and timeout notifications. Log and persist notifications to support reconciliation.
- Add observability and retry strategies around tax remittance calls and webhook processing to handle transient failures.
Related Documentation
- 📡 Webhook Setup Guide - Best practices for building reliable endpoints
- 🏗️ Production Setup - Go-live checklist, security and monitoring