---
title: Quickstart
description: Run the helper in mock mode, send a payment prompt, and simulate Safaricom's callback in about five minutes.
url: https://mpesa-stk-helper.thally.app/quickstart
---

# Quickstart

Run the helper in mock mode, send a payment prompt, and simulate Safaricom's callback in about five minutes.

In this quickstart you'll run the helper locally in mock mode, send an STK Push, and simulate Safaricom's callback to see a payment complete. Mock mode returns realistic responses without contacting Safaricom, so you don't need a Daraja account.

### Before you begin

- Python 3.10 or later
- Git

#### Clone and install

    ```bash
    git clone https://github.com/willen-gitonga/mpesa-stk-helper.git
    cd mpesa-stk-helper
    python3 -m venv virtual
    source virtual/bin/activate
    pip install -r requirements.txt
    ```

    On Windows, create and activate the virtual environment with `python -m venv virtual` and `virtual\Scripts\activate` instead.

#### Create the env file

    ```bash
    cp .env.example .env
    ```

    On Windows, use `copy .env.example .env`. The defaults set `MOCK_MODE=true`, so you don't need to change anything.

#### Start the server

    Run this from the project root, the folder that contains `requirements.txt`:

    ```bash
    uvicorn app.main:app --reload
    ```

    The output ends with:

    ```text
    INFO:     Application startup complete.
    ```

    Leave this terminal running and open a second one for the next steps.

#### Send a payment prompt

    ```bash
    curl -X POST http://127.0.0.1:8000/stk-push \
      -H "Content-Type: application/json" \
      -d '{"phone_number": "0712345678", "amount": 1}'
    ```

    The response shows the payment as `pending`, with your phone number converted to Daraja's format:

    ```json
    {
      "checkout_request_id": "ws_CO_MOCK_8F3A2C91B7D4",
      "phone_number": "254712345678",
      "amount": 1,
      "status": "pending",
      "result_desc": null,
      "receipt_number": null
    }
    ```

    Your `checkout_request_id` will be different. Copy it for the next step.

#### Simulate Safaricom's callback

    In a real payment, Safaricom calls your server when the customer approves or cancels the prompt. Here you send that callback yourself.

    Open `samples/callback.json`, replace `REPLACE_ME` with your `checkout_request_id`, and save the file. Then send it:

    ```bash
    curl -X POST http://127.0.0.1:8000/callback/change-me \
      -H "Content-Type: application/json" \
      -d @samples/callback.json
    ```

    The server acknowledges it the way Safaricom expects:

    ```json
    {"ResultCode": 0, "ResultDesc": "Accepted"}
    ```

    `change-me` is the default `CALLBACK_SECRET`. The guide explains why the callback URL contains a secret.

#### Check the payment

    Replace `YOUR_CHECKOUT_REQUEST_ID` with your value:

    ```bash
    curl http://127.0.0.1:8000/payments/YOUR_CHECKOUT_REQUEST_ID
    ```

    The payment is now `completed`, with the receipt number from the callback:

    ```json
    {
      "checkout_request_id": "ws_CO_MOCK_8F3A2C91B7D4",
      "phone_number": "254712345678",
      "amount": 1,
      "status": "completed",
      "result_desc": "The service request is processed successfully.",
      "receipt_number": "SAMPLE1234"
    }
    ```

You've sent a payment prompt and processed its result, the full STK Push flow.

**Prefer a browser?** Open `http://127.0.0.1:8000/docs` to try every endpoint from the interactive API page. This is also the easiest option on Windows PowerShell, where the `curl` commands above need different quoting.

## Troubleshooting

| Symptom | Cause | Fix |
|---|---|---|
| `pip: command not found` | Some Python installs, such as Homebrew's on macOS, don't provide a bare `pip` outside a virtual environment. | Create and activate the virtual environment first, as in step 1. |
| `Error loading ASGI app. Could not import module "main"` | The command doesn't match the project layout. The app is in `app/main.py`. | Run `uvicorn app.main:app --reload` from the project root, not from inside `app/`. |
| `422` with "Use a Kenyan mobile number" | The phone number isn't a valid Kenyan mobile number. | Use a format like `0712345678` or `254712345678`. |
| `404` with "Payment not found" | The ID is wrong, or the server restarted and lost its in-memory payments. | Send a new payment prompt and use the new `checkout_request_id`. |

## Next steps

- [Receive payment callbacks from the Daraja sandbox](/guides/sandbox-callbacks): send real prompts through Safaricom and handle the results.
- [API reference](/api/stk-push): full request and response details for each endpoint.