PayFlow API
REST API  ·  v2.0

PayFlow API Docs

Integrate M-Pesa STK Push payments into your app in minutes. Simple, reliable, and production-ready.

M-Pesa Daraja Real-time callbacks JSON responses Sandbox & Production

Overview

The PayFlow API lets you initiate M-Pesa STK Push payments, query transaction statuses, and retrieve transaction history — all via a simple REST interface. Every response is JSON, every request requires your API credentials in the headers.

Base URL

BASE
https://payflow.top/api/v2/

All endpoints are relative to this base URL. Always use HTTPS — plain HTTP requests will be rejected.

Authentication

Every request must include the following HTTP headers. Find your credentials in your PayFlow dashboard under API Keys.

  • X-API-Key Your unique public API key
  • X-API-Secret Your private API secret — keep this confidential
  • Content-Type: application/json Required for all POST requests

Example header

HTTP
POST /api/v2/stkpush.php HTTP/1.1
Host: payflow.top
X-API-Key: pk_live_xxxxxxxxxxxx
X-API-Secret: sk_live_xxxxxxxxxxxx
Content-Type: application/json

Quick Start

Pick your language and make your first API call in under a minute.

PHP
<?php
function initiatePayment(string $apiKey, string $apiSecret, array $payload): array {
    $ch = curl_init('https://payflow.top/api/v2/stkpush.php');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_HTTPHEADER     => [
            'X-API-Key: '    . $apiKey,
            'X-API-Secret: ' . $apiSecret,
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode($payload),
    ]);
    $response = curl_exec($ch);
    curl_close($ch);
    return json_decode($response, true);
}

$result = initiatePayment(
    apiKey:    'pk_live_xxxxxxxxxxxx',
    apiSecret: 'sk_live_xxxxxxxxxxxx',
    payload:   [
        'payment_account_id' => 17,
        'phone'              => '254712345678',
        'amount'             => 100,
        'reference'          => 'ORDER_001',
        'description'        => 'Product payment',
    ]
);

if ($result['success']) {
    echo 'STK sent! Checkout ID: ' . $result['checkout_request_id'];
} else {
    echo 'Error: ' . $result['message'];
}
?>
JavaScript (fetch)
async function initiatePayment({ apiKey, apiSecret, payload }) {
  const res = await fetch('https://payflow.top/api/v2/stkpush.php', {
    method: 'POST',
    headers: {
      'X-API-Key':    apiKey,
      'X-API-Secret': apiSecret,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(payload),
  });

  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}

// Usage
const result = await initiatePayment({
  apiKey:    'pk_live_xxxxxxxxxxxx',
  apiSecret: 'sk_live_xxxxxxxxxxxx',
  payload: {
    payment_account_id: 17,
    phone:       '254712345678',
    amount:      100,
    reference:   'ORDER_001',
    description: 'Product payment',
  },
});

console.log(result.checkout_request_id);
cURL
curl -X POST https://payflow.top/api/v2/stkpush.php \
  -H "X-API-Key: pk_live_xxxxxxxxxxxx" \
  -H "X-API-Secret: sk_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "payment_account_id": 17,
    "phone": "254712345678",
    "amount": 100,
    "reference": "ORDER_001",
    "description": "Product payment"
  }'

STK Push

Initiates an M-Pesa STK Push prompt on the customer's phone. The customer sees a PIN entry dialog and confirms the payment.

POST /stkpush.php Initiate a payment prompt

Request parameters

Parameter Type Required Description
payment_account_id integer Required Your PayFlow payment account ID
phone string Required Customer phone in 2547XXXXXXXX format
amount float Required Amount in KES (minimum 1)
reference string Optional Your internal order / reference ID
description string Optional Short description shown to customer

Responses

200 — Success
JSON
{
  "success": true,
  "message": "STK Push sent successfully",
  "checkout_request_id": "ws_CO_191020231234567890",
  "merchant_request_id": "29115-34620561-1",
  "transaction_id": 4821
}
400 / 401 — Error
JSON
{
  "success": false,
  "message": "Invalid API credentials",
  "error_code": "AUTH_FAILED"
}

Check Status

Query the current status of a transaction using the checkout_request_id returned by the STK Push endpoint.

POST /status.php Query a transaction's status

Request parameters

Parameter Type Required Description
checkout_request_id string Required The ID returned by the STK Push endpoint

Success response

200 — Success
JSON
{
  "success": true,
  "status": "completed",
  "amount": 100,
  "phone": "254712345678",
  "mpesa_receipt": "RAK74J8YAZ",
  "transaction_date": "2024-10-19 14:32:11"
}

Possible status values: pending, completed, failed, cancelled. Poll every 3–5 seconds until the status is no longer pending.

Transactions

Retrieve a paginated list of all transactions for your account.

GET /transactions.php List paginated transactions

Query parameters

Parameter Type Default Description
page integer 1 Page number
limit integer 10 Results per page (max 100)
status string Filter by status: completed, failed, pending

Success response

200 — Success
JSON
{
  "success": true,
  "data": [
    {
      "id": 4821,
      "phone": "254712345678",
      "amount": 100,
      "status": "completed",
      "mpesa_receipt": "RAK74J8YAZ",
      "reference": "ORDER_001",
      "created_at": "2024-10-19 14:32:11"
    }
  ],
  "pagination": {
    "current_page": 1,
    "per_page": 10,
    "total_items": 342,
    "total_pages": 35
  }
}

Live Tester

Test the API directly from your browser using real credentials. No setup required.

API Playground

Requests are sent to the live PayFlow API — use real credentials

Console
00:00 Ready. Enter your credentials and click a button above.

Error Codes

All errors return a JSON object with success: false, a human-readable message, and an error_code for programmatic handling.

HTTP Error Code Description
401 AUTH_FAILED API key or secret is invalid or missing
400 INVALID_PHONE Phone number format is incorrect
400 INVALID_AMOUNT Amount is below minimum or non-numeric
404 ACCOUNT_NOT_FOUND The provided payment_account_id does not exist
429 RATE_LIMITED Too many requests — slow down and retry
500 MPESA_ERROR Upstream M-Pesa Daraja API error