Developer Documentation

SparkPesa API Reference

Accept M-Pesa payments (Kenya) and Mobile Money (Uganda) in your application with a single integration.

Getting Started

Base URL

https://www.sparkpesa.io/api/v1

Quick Start

  1. 1Sign up at sparkpesa.io and create an organization
  2. 2Create a KES wallet (M-Pesa) or UGX wallet (Uganda Mobile Money)
  3. 3Generate an API key from Settings → API Keys
  4. 4Save your API Key and API Secret — the secret is shown only once
  5. 5Note your Wallet ID (UUID) or Wallet Code (8-digit number)

Supported Currencies & Providers

CurrencyCountryProviderOperations
KES
KenyaM-PesaSTK Push, B2C, Buy Goods, PayBill
UGX
UgandaMTN / AirtelRequest Payment, Send Payment

Authentication

Every API request requires three headers:

HeaderDescription
AuthorizationBearer <your_api_key>
X-SignatureHMAC-SHA256 signature of the request
X-TimestampUnix timestamp in seconds

Generating the Signature

The signature is an HMAC-SHA256 hash of a concatenated string using your API Secret as the key.

Signature string format: {apiKey}{timestamp}{walletCode}{currency}{phoneNumber}{amount}{accountReference}

Omit any field not in the request (use empty string). Field order is fixed.

javascript
const crypto = require('crypto');

const apiKey = 'sk_live_abc123def456...';
const apiSecret = 'ss_live_xyz789...';
const timestamp = Math.floor(Date.now() / 1000);

const payload = {
  walletCode: '12345678',
  currency: 'KES',
  phoneNumber: '254712345678',
  amount: 100,
  accountReference: 'ORDER-001',
};

// Build signature string
const signatureString = `${apiKey}${timestamp}${payload.walletCode}${payload.currency}${payload.phoneNumber}${payload.amount}${payload.accountReference}`;

// Generate HMAC-SHA256 signature
const signature = crypto
  .createHmac('sha256', apiSecret)
  .update(signatureString)
  .digest('hex');

const response = await fetch('https://www.sparkpesa.io/api/v1/payments/request-payment', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${apiKey}`,
    'X-Signature': signature,
    'X-Timestamp': timestamp.toString(),
  },
  body: JSON.stringify(payload),
});

Timestamp Validation

Requests rejected if timestamp differs from server time by more than 5 minutes.

Rate Limiting

Default 100 req/min per API key. Custom limits configurable.

Wallet Identification

All endpoints accept either a wallet UUID or an 8-digit wallet code:

Option A: Wallet UUID

json
{ "walletId": "550e8400-e29b-41d4-a716-446655440000" }

Option B: Wallet Code (human-friendly)

json
{ "walletCode": "12345678" }

At least one must be provided. If both are provided, walletId takes precedence.

Wallet Management

New

Create, list, update, and delete wallets programmatically via the API. Wallet management endpoints use a simplified HMAC signature.

Management Signature: {apiKey}{timestamp}

Unlike payment endpoints, wallet management uses only apiKey + timestamp for the signature — no body fields required.

Read Permissions

GET endpoints require wallets:view or wallet:balance

Write Permissions

POST, PATCH, DELETE require wallets:manage

List Wallets

GET
/v1/wallets

Returns all wallets belonging to the authenticated organization.

bash
curl -X GET https://www.sparkpesa.io/api/v1/wallets \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Signature: $SIGNATURE" \
  -H "X-Timestamp: $TIMESTAMP"

Create Wallet

POST
/v1/wallets

Create a new wallet. An 8-digit wallet code is auto-generated.

FieldTypeRequiredDescription
namestring
Required
Wallet name (1–255 characters)
currencystring
Required
3-letter ISO currency code (e.g. KES, UGX, USD)
descriptionstringOptionalWallet description (max 500 characters)
walletTypestringOptional"business" (default), "personal", or "escrow"
json
{
  "name": "Uganda Operations",
  "currency": "UGX",
  "description": "Wallet for Uganda mobile money",
  "walletType": "business"
}

Get Wallet Details

GET
/v1/wallets/:walletId

Returns detailed wallet information including the 5 most recent transactions.

bash
curl -X GET https://www.sparkpesa.io/api/v1/wallets/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Signature: $SIGNATURE" \
  -H "X-Timestamp: $TIMESTAMP"

Update Wallet

PATCH
/v1/wallets/:walletId

Update a wallet's name, description, or webhook URL. At least one field must be provided.

FieldTypeRequiredDescription
namestringOptionalNew wallet name (1–255 characters)
descriptionstringOptionalNew description (max 500 characters)
webhookUrlstringOptionalNew webhook URL (or null to remove)
json
{
  "name": "Renamed Wallet",
  "description": "Updated description",
  "webhookUrl": "https://yourapp.com/webhooks/new"
}

Delete Wallet

DELETE
/v1/wallets/:walletId

Deactivate (soft-delete) a wallet. The wallet must have a zero balance and must not be the default wallet.

bash
curl -X DELETE https://www.sparkpesa.io/api/v1/wallets/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Signature: $SIGNATURE" \
  -H "X-Timestamp: $TIMESTAMP"
Constraints: Returns 400 if the wallet is the default wallet or has a remaining balance/pending transactions.

Wallet Transfer

New

Transfer funds between two wallets in the same organization. Both wallets must use the same currency. Transfers are atomic — either both wallets update or neither does.

POST
/v1/wallets/transfer
Signature: Uses the management signature format — {apiKey}{timestamp}
FieldTypeRequiredDescription
sourceWalletIdstring (UUID)
Required
Wallet to debit funds from
destinationWalletIdstring (UUID)
Required
Wallet to credit funds to
amountnumber
Required
Amount to transfer (must be > 0)
descriptionstringOptionalTransfer description (max 255 chars)
referencestringOptionalCustom reference (auto-generated if omitted)
json
{
  "sourceWalletId": "550e8400-e29b-41d4-a716-446655440000",
  "destinationWalletId": "660f9511-f30c-52e5-b827-557766551111",
  "amount": 5000,
  "description": "Funds reallocation to Uganda ops",
  "reference": "TRF-REALLOC-001"
}

Same Currency

Both wallets must use the same currency (e.g. KES → KES).

Atomic

Debit and credit happen in a single database transaction — no partial transfers.

Instant

Transfers complete immediately with status completed.

Wallet Transactions

Retrieve a paginated list of transactions for a specific wallet. Supports filtering by status and type.

GET
/v1/wallets/:walletId/transactions

Query Parameters

FieldTypeRequiredDescription
pagenumberOptionalPage number (default: 1)
limitnumberOptionalItems per page, 1–100 (default: 20)
statusstringOptionalFilter: pending, processing, completed, failed, cancelled
typestringOptionalFilter: payment_in, payment_out, transfer, fee, refund
bash
curl -X GET "https://www.sparkpesa.io/api/v1/wallets/550e8400-e29b-.../transactions?page=1&limit=10&status=completed" \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Signature: $SIGNATURE" \
  -H "X-Timestamp: $TIMESTAMP"

Ordering

Transactions are returned newest first (createdAt DESC).

Combining Filters

Use both status and type together, e.g. ?status=completed&type=transfer.

Request Payment

KES

Initiate an STK Push to collect payment from a customer's M-Pesa account.

POST
/v1/payments/request-payment
FieldTypeRequiredDescription
walletIdstring
Either *
Wallet UUID
walletCodestring
Either *
8-digit wallet code
phoneNumberstring
Required
Customer phone 254XXXXXXXXX
amountnumber
Required
Amount in KES (10 – 250,000)
currencystring
Required
Must be "KES"
accountReferencestring
Required
Your order/invoice reference
transactionDescstringOptionalDescription shown to customer
callbackUrlstringOptionalWebhook URL for notification
metadataobjectOptionalCustom key-value pairs
json
{
  "walletCode": "12345678",
  "phoneNumber": "254712345678",
  "amount": 500,
  "currency": "KES",
  "accountReference": "INV-2026-001",
  "transactionDesc": "Payment for Invoice #001",
  "callbackUrl": "https://yourapp.com/webhooks/sparkpesa"
}
Flow: Customer receives STK Push → enters PIN → SparkPesa receives callback → wallet credited → webhook sent to your callbackUrl.

Send Payment

KES
B2C

Send money from your wallet to a customer's M-Pesa account.

POST
/v1/payments/send-payment
FieldTypeRequiredDescription
walletIdstring
Either *
Wallet UUID
walletCodestring
Either *
8-digit wallet code
phoneNumberstring
Required
Recipient phone 254XXXXXXXXX
amountnumber
Required
Amount in KES (10 – 250,000)
currencystring
Required
Must be "KES"
commandIdstringOptionalSalaryPayment, BusinessPayment, or PromotionPayment
descriptionstringOptionalPayment description
remarksstringOptionalM-Pesa remarks
callbackUrlstringOptionalWebhook URL for notification
metadataobjectOptionalCustom key-value pairs
json
{
  "walletCode": "12345678",
  "phoneNumber": "254712345678",
  "amount": 5000,
  "currency": "KES",
  "commandId": "SalaryPayment",
  "description": "March 2026 salary",
  "callbackUrl": "https://yourapp.com/webhooks/sparkpesa"
}

Sufficient wallet balance (amount + estimated fee) is required. Funds are reserved immediately.

Buy Goods

KES

Pay to an M-Pesa Till Number (Buy Goods).

POST
/v1/payments/buy-goods
FieldTypeRequiredDescription
walletIdstring
Either *
Wallet UUID
walletCodestring
Either *
8-digit wallet code
tillNumberstring
Required
5–7 digit till number
amountnumber
Required
Amount in KES (1 – 999,999)
currencystring
Required
Must be "KES"
accountReferencestring
Required
Payment reference
requesterPhonestring
Required
Requester phone 254XXXXXXXXX
remarksstringOptionalPayment remarks
callbackUrlstringOptionalWebhook URL
json
{
  "walletCode": "12345678",
  "tillNumber": "518518",
  "amount": 2500,
  "currency": "KES",
  "accountReference": "PO-2026-042",
  "requesterPhone": "254712345678",
  "remarks": "Office supplies"
}

PayBill

KES

Pay to an M-Pesa PayBill Number with an account number.

POST
/v1/payments/paybill
FieldTypeRequiredDescription
walletIdstring
Either *
Wallet UUID
walletCodestring
Either *
8-digit wallet code
paybillNumberstring
Required
5–7 digit paybill number
accountNumberstring
Required
Account/reference number
amountnumber
Required
Amount in KES (1 – 999,999)
currencystring
Required
Must be "KES"
requesterPhonestring
Required
Requester phone 254XXXXXXXXX
remarksstringOptionalPayment remarks
callbackUrlstringOptionalWebhook URL
json
{
  "walletCode": "12345678",
  "paybillNumber": "320320",
  "accountNumber": "12345678901",
  "amount": 3500,
  "currency": "KES",
  "requesterPhone": "254712345678",
  "remarks": "KPLC electricity"
}

Request Payment

UGX

Collect payment from a customer's mobile money account in Uganda (MTN / Airtel).

POST
/v1/payments/request-payment-uganda
FieldTypeRequiredDescription
walletIdstring
Either *
Wallet UUID
walletCodestring
Either *
8-digit wallet code
phoneNumberstring
Required
Uganda phone 256XXXXXXXXX
amountnumber
Required
Amount in UGX (1,000 – 5,000,000)
currencystring
Required
Must be "UGX"
accountReferencestring
Required
Your order/invoice reference
transactionDescstringOptionalPayment description
callbackUrlstringOptionalWebhook URL
metadataobjectOptionalCustom key-value pairs
json
{
  "walletCode": "87654321",
  "phoneNumber": "256712345678",
  "amount": 50000,
  "currency": "UGX",
  "accountReference": "ORDER-UG-001",
  "transactionDesc": "Payment for groceries"
}

Send Payment

UGX

Send money to a customer's mobile money account in Uganda.

POST
/v1/payments/send-payment-uganda
FieldTypeRequiredDescription
walletIdstring
Either *
Wallet UUID
walletCodestring
Either *
8-digit wallet code
phoneNumberstring
Required
Recipient phone 256XXXXXXXXX
amountnumber
Required
Amount in UGX (1,000 – 5,000,000)
currencystring
Required
Must be "UGX"
descriptionstring
Required
Payment description (required)
callbackUrlstringOptionalWebhook URL
metadataobjectOptionalCustom key-value pairs
json
{
  "walletCode": "87654321",
  "phoneNumber": "256712345678",
  "amount": 100000,
  "currency": "UGX",
  "description": "Freelance payment for March"
}

Webhooks

When a transaction completes or fails, SparkPesa sends a signed HTTP POST to your callbackUrl.

Event Types

EventDescription
payment.completedCustomer payment received and wallet credited
payment.failedCustomer payment failed
payout.completedMoney sent to recipient successfully
payout.failedPayout to recipient failed

Webhook Payload

json
{
  "event": "payment.completed",
  "transactionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "reference": "STK-1710000000-abc123def",
  "status": "completed",
  "amount": 500,
  "currency": "KES",
  "timestamp": "2026-03-17T11:30:00.000Z",
  "data": {
    "phoneNumber": "254712345678",
    "mpesaReceiptNumber": "UCA668UDWH",
    "transactionDate": "20260317113000",
    "accountReference": "INV-2026-001"
  }
}

Signature Verification

Always verify the X-SparkPesa-Signature header before processing:

javascript
const crypto = require('crypto');

app.post('/webhooks/sparkpesa', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-sparkpesa-signature'];
  const rawBody = req.body.toString();

  const expected = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(rawBody).digest('hex');

  if (!crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expected, 'hex')
  )) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(rawBody);

  switch (event.event) {
    case 'payment.completed':
      // Credit user, fulfill order
      break;
    case 'payout.completed':
      // Payout confirmed
      break;
  }

  res.status(200).json({ received: true });
});
Retry Policy: Failed deliveries are retried 3 times with exponential backoff (immediate → 30s → 2min). Return 200 OK to acknowledge.

Error Handling

HTTP Status Codes

CodeMeaning
200
Success
400
Bad request / validation error
401
Invalid API key or signature
403
Insufficient permissions or wallet access denied
404
Wallet or resource not found
429
Rate limit exceeded
500
Server error

Error Response Format

json
{
  "error": "Validation failed",
  "details": [
    { "field": "phoneNumber", "message": "Phone number must be in format 254XXXXXXXXX" },
    { "field": "amount", "message": "Amount must be greater than 0" }
  ]
}

Common Errors

ErrorCauseFix
Missing X-Signature headerNo HMAC signatureAdd signature generation
Request timestamp is too oldClock drift > 5 minSync system clock
Invalid API key or signatureWrong key/secret/sig stringCheck signature construction
Currency mismatchWallet currency ≠ requestUse matching wallet
Insufficient wallet balanceNot enough fundsTop up wallet balance
Access denied to specified walletAPI key scopeCheck wallet permissions

Code Examples

Full client SDK examples with authentication, all payment methods, and error handling.

typescript
import crypto from 'crypto';

class SparkPesaClient {
  private apiKey: string;
  private apiSecret: string;
  private baseUrl: string;

  constructor(apiKey: string, apiSecret: string, baseUrl = 'https://www.sparkpesa.io/api/v1') {
    this.apiKey = apiKey;
    this.apiSecret = apiSecret;
    this.baseUrl = baseUrl;
  }

  private sign(payload: Record<string, any>) {
    const timestamp = Math.floor(Date.now() / 1000);
    const sigString = [
      this.apiKey, timestamp,
      payload.walletCode ?? '', payload.currency ?? '',
      payload.phoneNumber ?? '', payload.amount ?? '',
      payload.accountReference ?? '',
    ].join('');
    const signature = crypto.createHmac('sha256', this.apiSecret)
      .update(sigString).digest('hex');
    return { signature, timestamp };
  }

  private async post(endpoint: string, payload: Record<string, any>) {
    const { signature, timestamp } = this.sign(payload);
    const res = await fetch(`${this.baseUrl}${endpoint}`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${this.apiKey}`,
        'X-Signature': signature,
        'X-Timestamp': timestamp.toString(),
      },
      body: JSON.stringify(payload),
    });
    const data = await res.json();
    if (!res.ok) throw new Error(data.error || 'Request failed');
    return data;
  }

  // Collect M-Pesa payment
  async requestPayment(walletCode: string, phone: string, amount: number, ref: string) {
    return this.post('/payments/request-payment', {
      walletCode, phoneNumber: phone, amount, currency: 'KES', accountReference: ref,
    });
  }

  // Send M-Pesa B2C payout
  async sendPayment(walletCode: string, phone: string, amount: number) {
    return this.post('/payments/send-payment', {
      walletCode, phoneNumber: phone, amount, currency: 'KES', commandId: 'BusinessPayment',
    });
  }

  // Pay to Till Number
  async buyGoods(walletCode: string, till: string, amount: number, ref: string, phone: string) {
    return this.post('/payments/buy-goods', {
      walletCode, tillNumber: till, amount, currency: 'KES', accountReference: ref, requesterPhone: phone,
    });
  }

  // Pay to PayBill Number
  async payBill(walletCode: string, paybill: string, acc: string, amount: number, phone: string) {
    return this.post('/payments/paybill', {
      walletCode, paybillNumber: paybill, accountNumber: acc, amount, currency: 'KES', requesterPhone: phone,
    });
  }
}

// Usage
const client = new SparkPesaClient('sk_live_...', 'ss_live_...');
const result = await client.requestPayment('12345678', '254712345678', 500, 'ORDER-001');
console.log('Transaction:', result.transactionId);

Testing

Use sandbox credentials for testing. Sandbox transactions are simulated and do not interact with real payment providers.

Test Values

FieldKenya (KES)Uganda (UGX)
Phone Number254708374149256712345678
Amount Range10 – 250,0001,000 – 5,000,000
Till Numbers247247, 518518N/A
PayBill320320 (acc: 12345678901)N/A

Testing Checklist

AI IDE Integration (MCP)

New

SparkPesa provides an MCP (Model Context Protocol) server that lets AI assistants in IDEs like Windsurf, Cursor, and VS Code (Copilot) interact with the SparkPesa API directly — collect payments, send payouts, generate code, and more, all from your AI chat.

Available Tools

ToolDescription
request_payment_kenyaCollect M-Pesa payment via STK Push (KES)
send_payment_kenyaSend M-Pesa B2C payout (KES)
buy_goodsPay to M-Pesa Till Number (KES)
paybillPay to M-Pesa PayBill Number (KES)
request_payment_ugandaCollect Mobile Money payment (UGX)
send_payment_ugandaSend Mobile Money payout (UGX)
list_walletsList all wallets for your organization
get_walletGet wallet details and recent transactions
create_walletCreate a new wallet (auto-generates 8-digit code)
update_walletRename a wallet or update description/webhook URL
delete_walletDeactivate a wallet (soft-delete)
transfer_fundsTransfer funds between two wallets (same currency)
list_transactionsList paginated transactions for a wallet with filters
generate_signatureGenerate HMAC signature for debugging
generate_webhook_handlerGenerate webhook handler code (Express, Next.js, Flask, FastAPI)
generate_client_sdkGenerate full client SDK (TypeScript, JavaScript, Python, cURL)

Setup

The MCP server is published on npm. No installation needed — your IDE runs it automatically via npx.

  1. 1Get your API Key and API Secret from Settings → API Keys
  2. 2Add the MCP server config to your IDE (see below)
  3. 3Restart your IDE — the SparkPesa tools will be available in AI chat

npm package

@sparkpesa/mcp-server

Or install globally: npm install -g @sparkpesa/mcp-server

IDE Configuration

Add to ~/.codeium/windsurf/mcp_config.json:

json
{
  "mcpServers": {
    "sparkpesa": {
      "command": "npx",
      "args": ["-y", "@sparkpesa/mcp-server"],
      "env": {
        "SPARKPESA_API_KEY": "sk_live_your_api_key",
        "SPARKPESA_API_SECRET": "ss_live_your_api_secret"
      }
    }
  }
}

Usage Examples

Once configured, you can ask your AI assistant natural-language questions and it will use the SparkPesa tools:

"Collect KES 500 from 254712345678 for order ORDER-001 using wallet 12345678"
"Send KES 5000 salary payment to 254712345678"
"Pay KPLC electricity bill — paybill 320320, account 12345678901, KES 3500"
"List all my wallets"
"Create a new UGX wallet called Uganda Operations"
"Rename wallet abc-123 to Main KES Wallet"
"Transfer KES 5000 from Main Wallet to Operations Wallet"
"Generate a Next.js webhook handler for SparkPesa"

Built-in Resources

The MCP server also exposes reference documentation as resources that AI assistants can read for context:

Resource URIContents
sparkpesa://docs/overviewAPI overview, endpoints, authentication
sparkpesa://docs/webhooksWebhook events, payloads, signature verification
sparkpesa://docs/errorsError codes, troubleshooting guide
Environment Variables: Set SPARKPESA_API_KEY and SPARKPESA_API_SECRET in your IDE's MCP config. Optionally set SPARKPESA_BASE_URL to override the default API base URL.