> ## Documentation Index
> Fetch the complete documentation index at: https://developers.kulmipay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# POST /api/v1/payment/mpesa-stk-push

> Trigger an M-Pesa STK push prompt to a customer's Safaricom phone number using your Bearer token. The customer confirms payment by entering their M-Pesa PIN.

Use this endpoint to send an M-Pesa STK push (Lipa na M-Pesa Express) prompt directly to a customer's phone. When you call this endpoint, the customer receives a payment dialog on their device and approves the charge with their M-Pesa PIN. No redirect or frontend action is required.

This endpoint requires your **secret key** and is intended for server-to-server calls only. If you need to trigger M-Pesa from a client-side context, use the [collect payment endpoint](/api-reference/collect-payment) with your `public_key` instead.

**Method:** `POST`\
**URL:** `https://app.kulmipay.com/api/v1/payment/mpesa-stk-push/`

## Authentication

```http theme={null}
Authorization: Bearer <secret_key>
```

## Request

<ParamField body="phone_number" type="string" required>
  Customer's M-Pesa phone number in `254XXXXXXXXX` format. The number must be a registered Safaricom line with an active M-Pesa account.
</ParamField>

<ParamField body="amount" type="number" required>
  Amount to charge in KES. Decimal values are rounded up to the nearest whole shilling before the STK push is sent. Account and M-Pesa limits may apply.
</ParamField>

<ParamField body="api_ref" type="string">
  Your reference for this transaction, up to 140 characters. Stored on the invoice and returned in webhook events. Defaults to `"MPesa Express"` if omitted.
</ParamField>

<ParamField body="wallet_id" type="string">
  Alias ID of a specific KES wallet to receive the funds. If omitted, funds go to your default KES settlement wallet.
</ParamField>

<ParamField body="mobile_tarrif" type="string" default="BUSINESS-PAYS">
  Who bears the M-Pesa processing fee. `BUSINESS-PAYS` deducts the fee from your settlement; `CUSTOMER-PAYS` adds the fee on top of `amount` before sending the prompt. If your account has a default tariff set in Payment Settings, that value is used when this field is omitted.
</ParamField>

## Response

The response contains the payment session created for this request, including the invoice and customer details.

<ResponseField name="id" type="string">
  Payment session UUID.
</ResponseField>

<ResponseField name="invoice" type="object">
  Invoice created for this STK push.

  <Expandable title="invoice properties">
    <ResponseField name="invoice.invoice_id" type="string">
      Invoice alias ID. Use this with the [payment status endpoint](/api-reference/payment-status) to check whether the customer completed payment.
    </ResponseField>

    <ResponseField name="invoice.state" type="string">
      Invoice state. Starts as `PENDING` while the customer is interacting with the STK prompt. Changes to `COMPLETE` on successful payment or `FAILED` if the customer declines or the request times out.
    </ResponseField>

    <ResponseField name="invoice.value" type="string">
      Amount charged, in KES, after tariff adjustment.
    </ResponseField>

    <ResponseField name="invoice.provider" type="string">
      Always `M-PESA` for this endpoint.
    </ResponseField>

    <ResponseField name="invoice.api_ref" type="string">
      Your reference as submitted in the request.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="customer" type="object">
  Customer record associated with this session.

  <Expandable title="customer properties">
    <ResponseField name="customer.customer_id" type="string">
      Internal customer alias.
    </ResponseField>

    <ResponseField name="customer.phone_number" type="string">
      The phone number the STK push was sent to.
    </ResponseField>

    <ResponseField name="customer.provider" type="string">
      Always `M-PESA` for STK push sessions.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="created_at" type="string">
  ISO 8601 timestamp when the session was created.
</ResponseField>

## Code examples

<CodeGroup>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://app.kulmipay.com/api/v1/payment/mpesa-stk-push/ \
    --header 'Authorization: Bearer YOUR_SECRET_KEY' \
    --header 'Content-Type: application/json' \
    --data '{
      "phone_number": "254712345678",
      "amount": "1500.00",
      "api_ref": "order_20240416_001",
      "mobile_tarrif": "BUSINESS-PAYS"
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://app.kulmipay.com/api/v1/payment/mpesa-stk-push/",
      headers={
          "Authorization": "Bearer YOUR_SECRET_KEY",
          "Content-Type": "application/json",
      },
      json={
          "phone_number": "254712345678",
          "amount": "1500.00",
          "api_ref": "order_20240416_001",
          "mobile_tarrif": "BUSINESS-PAYS",
      },
  )

  data = response.json()
  invoice_id = data["invoice"]["invoice_id"]
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(
    "https://app.kulmipay.com/api/v1/payment/mpesa-stk-push/",
    {
      method: "POST",
      headers: {
        Authorization: "Bearer YOUR_SECRET_KEY",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        phone_number: "254712345678",
        amount: "1500.00",
        api_ref: "order_20240416_001",
        mobile_tarrif: "BUSINESS-PAYS",
      }),
    }
  );

  const data = await response.json();
  const invoiceId = data.invoice.invoice_id;
  ```
</CodeGroup>

## Example response

```json theme={null}
{
  "id": "a3f7c819-4e2b-4d1a-9c3f-2b8e1d7a6f05",
  "invoice": {
    "invoice_id": "GQ7KZ2XPNM",
    "state": "PENDING",
    "value": "1500.00",
    "currency": "KES",
    "provider": "M-PESA",
    "account": "254712345678",
    "api_ref": "order_20240416_001",
    "mpesa_reference": null,
    "created_at": "2024-04-16T08:23:11.042Z"
  },
  "customer": {
    "customer_id": "CUST_XJ82KP",
    "phone_number": "254712345678",
    "provider": "M-PESA",
    "created_at": "2024-04-16T08:23:11.012Z"
  },
  "customer_comment": null,
  "refundable": false,
  "created_at": "2024-04-16T08:23:11.055Z",
  "updated_at": "2024-04-16T08:23:11.055Z"
}
```

<Note>
  The STK push prompt expires if the customer does not respond in time. Poll the [payment status endpoint](/api-reference/payment-status) with the returned `invoice.invoice_id` to detect the final state, or subscribe to [webhook events](/webhooks/overview) to receive a push notification when the payment completes.
</Note>
