Wallet
Transactions
Account
Paysuit
HomeDaraja TesterDirect PaywallManaged PaywallPricingDocsMy Account

Paysuit Docs

Choose the integration style that matches your stack. You can be live in minutes.

Quick start: If you are in any JavaScript framework, run npm install paysuitjs. If you are in Python, run pip install paysuit-py. Method 3 is direct HTTP request payloads.

Method 1: JavaScript / TypeScript SDK

Works with Next.js, React, Node.js, Nuxt, SvelteKit, and more.

npm install paysuitjs
import { createMpesaClient } from "paysuitjs";

const client = createMpesaClient({
  baseUrl: "https://api.paysuit.co.ke",
  apiKey: process.env.PAYSUIT_API_KEY!,
});

const response = await client.stkPush({
  senderPhoneNumber: "254712345678",
  amount: "10",
  receiverBankPaybill: "174379",
  receiverBankAccountNumber: "INV-2026-001",
  transactionDescription: "Starter Plan",
});

console.log(response);

Method 2: Python SDK

Great for Django, Flask, FastAPI, and server automation.

pip install paysuit-py
from paysuit import MpesaClient

client = MpesaClient(
    base_url="https://api.paysuit.co.ke",
    api_key="YOUR_API_KEY"
)

response = client.stk_push(
    senderPhoneNumber="254712345678",
    amount="10",
    receiverBankPaybill="174379",
    receiverBankAccountNumber="INV-2026-001",
    transactionDescription="Starter Plan"
)

print(response)

Method 3: Direct API Request + Webhook

Send raw HTTP requests and receive final payment outcomes on your webhook URL.

curl -X POST https://api.paysuit.co.ke/api/v1/stk_push \
  -H "Content-Type: application/json" \
  -H "X-API-Key: <YOUR_API_KEY>" \
  -d '{
    "senderPhoneNumber": "254712345678",
    "amount": "10",
    "receiverBankPaybill": "174379",
    "receiverBankAccountNumber": "INV-2026-001",
    "transactionDescription": "Starter Plan",
    "callbackUrl": "https://yourapp.com/api/payments/webhook"
  }'
After sending STK Push, the final result should be consumed from your webhook endpoint so you can update your DB and notify the user in real time.
// app/api/payments/webhook/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
  const payload = await req.json();

  // Example callback fields:
  // payload.result_code, payload.result_description,
  // payload.CheckoutRequestID, payload.MerchantRequestID, payload.metadata

  if (payload.result_code === 0) {
    // Mark payment as successful in DB and unlock product/service for user
    // notifyUser(payload.CheckoutRequestID, "Payment successful ✅");
  } else {
    // notifyUser(payload.CheckoutRequestID, "Payment failed ❌");
  }

  // Always return 200 quickly so webhook retries don't pile up
  return NextResponse.json({ ok: true });
}

Reference SDK (TypeScript)

Starter implementation you can adapt if you want your own custom client package.

// src/index.ts
interface MpesaClientConfig {
  baseUrl: string;
  apiKey: string;
  timeout?: number;
}

interface StkPushRequest {
  senderPhoneNumber: string;
  amount: string;
  receiverBankPaybill?: string | null;
  receiverBankAccountNumber?: string | null;
  transactionDescription?: string;
}

interface StkPushResponse {
  MerchantRequestID: string;
  CheckoutRequestID: string;
  ResponseCode: string;
  ResponseDescription: string;
  CustomerMessage: string;
}

export class MpesaClient {
  private baseUrl: string;
  private apiKey: string;
  private timeout: number;

  constructor(config: MpesaClientConfig) {
    if (!config.baseUrl) throw new Error("baseUrl is required");
    if (!config.apiKey) throw new Error("apiKey is required");

    this.baseUrl = config.baseUrl.replace(/\/$/, "");
    this.apiKey = config.apiKey;
    this.timeout = config.timeout || 30000;
  }

  private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), this.timeout);

    try {
      const response = await fetch(this.baseUrl + endpoint, {
        ...options,
        headers: {
          "Content-Type": "application/json",
          "X-API-Key": this.apiKey,
          ...options.headers,
        },
        signal: controller.signal,
      });

      clearTimeout(timeoutId);
      const data = await response.json();

      if (!response.ok) {
        throw new Error(data.detail || data.message || "HTTP " + response.status);
      }

      return data as T;
    } catch (error) {
      clearTimeout(timeoutId);
      if (error instanceof Error && error.name === "AbortError") {
        throw new Error("Request timeout");
      }
      throw error;
    }
  }

  async stkPush(request: StkPushRequest): Promise<StkPushResponse> {
    return this.request<StkPushResponse>("/api/v1/stk_push", {
      method: "POST",
      body: JSON.stringify(request),
    });
  }
}

export function createMpesaClient(config: MpesaClientConfig): MpesaClient {
  return new MpesaClient(config);
}