What a callback is
By the end of this track you can receive and trust a result.
0/3 finished
Step 1
The idea
A callback is an HTTPS POST that Daraja sends to your URL with the result. The URL must be public HTTPS. Your handler replies fast with {"ResultCode": 0, "ResultDesc": "Accepted"} and does slow work later.
Step 2
How it works
What arrives
A callback is a plain HTTPS POST with a JSON body, sent by Safaricom to the URL you named in the request. It is the only place you get the receipt number for an STK Push.
Reading an STK callback
The useful values sit in Body.stkCallback. On success they are in CallbackMetadata.Item, a list of name and value pairs, not an object. On failure there is no metadata at all.
def parse_stk(body): cb = body["Body"]["stkCallback"] items = {i["Name"]: i.get("Value") for i in cb.get("CallbackMetadata", {}).get("Item", [])} return { "checkout_id": cb["CheckoutRequestID"], "result_code": int(cb["ResultCode"]), "receipt": items.get("MpesaReceiptNumber"), "amount": items.get("Amount"), }Reply fast, work later
Save the raw body, reply with a 200 and {"ResultCode": 0, "ResultDesc": "Accepted"}, and do the rest (update the order, send the email) in the background. Most callbacks are sent once with few or no retries, so a slow or failing handler can lose the result, and then only a query recovers it.
@app.post("/callbacks/stk")def stk_callback(): save_raw_event(request.get_data()) # first, so nothing is lost queue.enqueue(process_stk, request.get_json()) return {"ResultCode": 0, "ResultDesc": "Accepted"}Don't trust it blindly
Safaricom sends no auth headers, so anyone who learns your URL can POST to it. Use a hard-to-guess path, check that the CheckoutRequestID is one you created, and check the amount matches. When in doubt, confirm with STK Query.
Step 3
Try it
Take a sample callback body and parse it.
With the MCP server, ask your agent to use mpesa_parse_callback
Step 4
Read the response
ResultCode, MpesaReceiptNumber, Amount and CheckoutRequestID.
Step 5
Common mistakes
- Doing slow work before replying.
- Reading
CallbackMetadataon a failed payment and crashing because it isn't there. - Not saving the raw body. When something goes wrong, it's the only evidence you have.
Go deeper
Why reply before sending the customer an email?
Answer in your own words first.
Show the answerHide the answer
A slow reply times out, and Daraja retries.