McTaba Labs logo
Software EngineeringThe WhiteboardProjectsEventsMasterclassesWebinarsLearn
Book Call

500+ developers already in the community

Get answers, find accountability partners, and stay ahead

Join Discord →
McTaba Labs logo

Educating 1 million students in tech by 2030.

Programs

  • HighSchool Graduates Launchpad
  • AI & Business Automation
  • Corporate Training
  • McTaba Story & Mission
  • Full-Stack Curriculum
  • Course Pricing & Plans
  • Frequently Asked Questions

Resources

  • Join Discord Community
  • Tech Events in Nairobi
  • Masterclasses
  • Free Resources
  • Learn
  • Campus
  • Africa Tech Talent Report
  • Partner With Us
  • Start a Project
  • Our Work

Contact Us

  • 124 Manyani East Road, Lavington, Nairobi
  • +254727795603
  • Book a Discovery Call

Office Hours:

Mon-Fri: 9am - 2pm EAT

Sat: 10am - 12pm EAT

© 2027 McTaba Labs - A product of Edtech Industries. All rights reserved.

Privacy PolicyTerms of ServiceRefund Policy
  1. Learn
  2. Paystack for African Developers
  3. Transfers to M-PESA Wallets, Paybills and Tills
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 20, 2026

Transfers to M-PESA Wallets, Paybills and Tills

To send a Paystack transfer to an M-Pesa wallet, create a mobile money transfer recipient using the phone number in international format (254XXXXXXXXX). For Paybills, use the Paybill number as the account identifier with an optional account number. For Tills, use the Till number. Each type follows the standard transfer flow: create recipient, initiate transfer, and track via webhooks.

In this article

  1. 1. The M-Pesa Transfer Landscape
  2. 2. Transfers to M-Pesa Personal Wallets
  3. 3. Transfers to Paybill Business Numbers
  4. 4. Transfers to Till Numbers
  5. 5. Phone Number Formatting and Validation
  6. 6. M-Pesa-Specific Failure Modes
  7. 7. M-Pesa vs Bank Transfers: When to Use Which
  8. 8. Build for the Kenyan Market

The M-Pesa Transfer Landscape

In Kenya, M-Pesa is not just a mobile wallet. It is the financial backbone of the country. Over 90% of Kenyan adults use it. When you build a payout system for the Kenyan market, M-Pesa support is not optional.

Paystack supports transfers to three types of M-Pesa destinations:

  • Personal wallets: Regular M-Pesa accounts tied to a phone number. This is how you pay individuals: drivers, freelancers, vendors, employees. The money lands directly in their M-Pesa wallet.
  • Paybills: Business accounts with a shortcode (e.g., 888880). Used by companies, schools, utilities, and other organizations. Paybills often use account numbers to route payments internally.
  • Tills: Merchant payment numbers (e.g., 5xxxxxx). Used at physical retail locations or by small businesses. Simpler than Paybills because they do not use account numbers.

Each destination type has slightly different recipient creation requirements, but the transfer initiation process is the same across all three. For the overall transfer lifecycle, see how Paystack transfers work end to end.

Transfers to M-Pesa Personal Wallets

To send money to someone's M-Pesa wallet, create a mobile money recipient with their phone number in international format.

const createMpesaRecipient = async (name, phoneNumber) => {
  // Normalize phone number to 254XXXXXXXXX
  var normalized = normalizeKenyanPhone(phoneNumber);

  var response = await fetch('https://api.paystack.co/transferrecipient', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      type: 'mobile_money',
      name: name,
      account_number: normalized,
      bank_code: 'MPESA',
      currency: 'KES',
    }),
  });

  var data = await response.json();
  if (!data.status) {
    throw new Error('M-Pesa recipient creation failed: ' + data.message);
  }

  return data.data;
};

// Phone number normalization
function normalizeKenyanPhone(phone) {
  // Remove spaces, dashes, and plus signs
  var cleaned = phone.replace(/[s-+]/g, '');

  // Convert 07xx to 2547xx
  if (cleaned.indexOf('07') === 0 || cleaned.indexOf('01') === 0) {
    return '254' + cleaned.substring(1);
  }

  // Already in 254 format
  if (cleaned.indexOf('254') === 0 && cleaned.length === 12) {
    return cleaned;
  }

  throw new Error('Invalid Kenyan phone number: ' + phone);
}

Once you have the recipient, initiate the transfer exactly as you would for a bank transfer:

var transfer = await fetch('https://api.paystack.co/transfer', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    source: 'balance',
    amount: 500000, // 5,000 KES in cents
    recipient: recipientCode,
    reason: 'Driver payout - Trip #4521',
    reference: 'payout_driver_42_trip4521',
  }),
});

M-Pesa wallet transfers are near-instant. The recipient sees the money and gets the standard M-Pesa SMS notification within seconds of the transfer being processed.

Get weekly developer tips

Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.

No spam. Unsubscribe anytime.

Transfers to Paybill Business Numbers

Paybills are M-Pesa business accounts identified by a shortcode (e.g., 888880). Many organizations use Paybills with account numbers to route payments internally. For example, a school's Paybill might use student IDs as account numbers.

const createPaybillRecipient = async (name, paybillNumber, accountNumber) => {
  var response = await fetch('https://api.paystack.co/transferrecipient', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      type: 'mobile_money',
      name: name,
      account_number: paybillNumber,
      bank_code: 'MPESA',
      currency: 'KES',
      metadata: {
        account_number: accountNumber, // Internal routing number
      },
    }),
  });

  var data = await response.json();
  return data.data;
};

Note: The exact field mapping for Paybill account numbers may vary depending on how Paystack implements Paybill transfers in Kenya. Check the current Paystack documentation for Kenya-specific recipient creation parameters, as the API evolves. The important thing is to capture both the Paybill number and the account number, because many Paybills reject payments without an account number.

Transfers to Till Numbers

Till numbers (also called Buy Goods numbers) are used by merchants for payments. They are simpler than Paybills because they do not use account numbers. The payment goes directly to the merchant identified by the Till number.

const createTillRecipient = async (merchantName, tillNumber) => {
  var response = await fetch('https://api.paystack.co/transferrecipient', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      type: 'mobile_money',
      name: merchantName,
      account_number: tillNumber,
      bank_code: 'MPESA',
      currency: 'KES',
    }),
  });

  var data = await response.json();
  return data.data;
};

Till transfers are useful when your platform needs to pay suppliers or service providers who operate physical businesses and only accept M-Pesa via Till number. The settlement is similar to wallet transfers in terms of speed.

Phone Number Formatting and Validation

Phone number formatting is the single most common cause of M-Pesa transfer failures. Users enter numbers in all sorts of formats: 0712345678, +254712345678, 254 712 345 678, 07-1234-5678. Your system needs to normalize all of these to 254XXXXXXXXX.

function validateAndNormalizePhone(input) {
  if (!input || typeof input !== 'string') {
    return { valid: false, error: 'Phone number is required' };
  }

  // Remove all non-digit characters
  var digits = input.replace(/D/g, '');

  // Handle different input formats
  if (digits.length === 10 && (digits.indexOf('07') === 0 || digits.indexOf('01') === 0)) {
    // Local format: 0712345678 => 254712345678
    digits = '254' + digits.substring(1);
  } else if (digits.length === 9 && (digits.indexOf('7') === 0 || digits.indexOf('1') === 0)) {
    // Without leading zero: 712345678 => 254712345678
    digits = '254' + digits;
  } else if (digits.length === 12 && digits.indexOf('254') === 0) {
    // Already international: 254712345678
    // Keep as is
  } else {
    return {
      valid: false,
      error: 'Phone number must be a valid Kenyan number (e.g., 0712345678 or 254712345678)',
    };
  }

  // Validate it starts with a known Safaricom prefix
  var safaricomPrefixes = ['2547', '2541'];
  var hasValidPrefix = false;
  for (var i = 0; i < safaricomPrefixes.length; i++) {
    if (digits.indexOf(safaricomPrefixes[i]) === 0) {
      hasValidPrefix = true;
      break;
    }
  }

  if (!hasValidPrefix) {
    return { valid: false, error: 'Number does not appear to be a Safaricom M-Pesa number' };
  }

  return { valid: true, normalized: digits };
}

Run this validation before creating the recipient and again before displaying the number to the admin for confirmation. Show the normalized number to the user: "We will send the payout to 254712345678. Is this correct?" This catches transposition errors that pass format validation but send money to the wrong person.

M-Pesa-Specific Failure Modes

M-Pesa transfers fail for reasons beyond the standard bank transfer failures.

Unregistered number

The phone number is not registered for M-Pesa. This happens when someone gives you their Airtel or Telkom number instead of their Safaricom number. The transfer fails because M-Pesa only works with Safaricom numbers.

Wallet limit exceeded

M-Pesa accounts have transaction and balance limits based on the account tier. If your transfer would push the recipient over their wallet limit, it fails. You cannot know the recipient's current balance or tier, so you cannot predict this failure. It requires the recipient to withdraw or upgrade their account.

Subscriber suspended

The M-Pesa account has been suspended by Safaricom due to fraud concerns, SIM replacement in progress, or other compliance reasons. The transfer fails, and the recipient needs to resolve the issue with Safaricom.

System timeout

Safaricom's M-Pesa system is occasionally slow or unavailable. This is a transient failure. Retry after 15-30 minutes.

For all M-Pesa failures, check the failure reason in the transfer.failed webhook. Transient failures (system timeout) can be retried. Permanent failures (unregistered number, suspended account) require recipient action. For a complete failure handling framework, see managing transfer failures and reversals.

M-Pesa vs Bank Transfers: When to Use Which

In Kenya, you often have a choice: pay to M-Pesa or pay to a bank account. The right choice depends on the use case.

Use M-Pesa wallet transfers when:

  • The recipient is an individual (driver, freelancer, casual worker)
  • Speed matters (M-Pesa is near-instant, bank transfers can take hours)
  • The recipient does not have a bank account (common in Kenya)
  • Transfer amounts are moderate (M-Pesa has wallet limits)

Use bank transfers when:

  • The recipient is a business with a corporate bank account
  • Transfer amounts are large (above M-Pesa wallet limits)
  • The recipient prefers bank settlement for accounting purposes
  • You need Pesalink for real-time bank-to-bank transfers

Many platforms offer recipients the choice. During onboarding, ask: "Do you want to be paid via M-Pesa or bank transfer?" Create the appropriate recipient type and store the preference. Some recipients want M-Pesa for daily payouts (drivers) and bank for monthly settlements (restaurants). Support both.

For bank-specific transfers in Kenya, see transfers to Kenyan bank accounts via Paystack. For instant bank transfers, see Pesalink transfers through Paystack.

Build for the Kenyan Market

M-Pesa transfers are essential for any product serving the Kenyan market. Getting the details right (phone formatting, Paybill account numbers, failure handling) is what separates a working product from a frustrating one.

The McTaba bootcamp covers M-Pesa integration in depth. You build real payment flows for the Kenyan market.

Learn M-Pesa integration at McTaba

Key Takeaways

  • ✓Paystack supports transfers to three M-Pesa destination types in Kenya: personal wallets, Paybill business accounts, and Till numbers.
  • ✓M-Pesa wallet recipients use the phone number in international format: 254XXXXXXXXX (no leading zero, no plus sign).
  • ✓M-Pesa wallet transfers are near-instant. The recipient gets the standard M-Pesa SMS notification within seconds.
  • ✓Paybill transfers may require both a business number and an account number, since many Paybills route payments internally using account numbers.
  • ✓Phone number formatting is the most common source of M-Pesa transfer failures. Validate and normalize to 254XXXXXXXXX before creating recipients.
  • ✓M-Pesa has transaction limits that depend on the recipient account tier. Large transfers may fail if they exceed the recipient wallet limit.

Frequently Asked Questions

Can I send M-Pesa transfers to Airtel Money or T-Kash numbers?
Paystack M-Pesa transfers are specific to Safaricom M-Pesa. To send to Airtel Money or Telkom T-Kash, you would need to use a different integration or transfer to a bank account that the recipient can then withdraw from.
What is the maximum amount I can send to an M-Pesa wallet?
M-Pesa wallet limits are set by Safaricom and depend on the recipient account tier. These limits change periodically. Check Safaricom current M-Pesa tariff and limits for the latest figures. If your transfer exceeds the recipient wallet balance limit, it fails.
Do M-Pesa transfers work on weekends and holidays?
Yes. M-Pesa operates 24/7, including weekends and public holidays. This is one of the advantages of M-Pesa over bank transfers, which may be delayed during non-business hours.
How do I know if a phone number is registered for M-Pesa?
You cannot check M-Pesa registration status through the Paystack API. The best approach is to attempt the transfer and handle the failure if the number is unregistered. You can also ask the recipient to confirm their M-Pesa number during onboarding.

Learn Paystack Integration

KES 2,999

The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.

Start Paystack Course

Not ready? Create Free Account

Keep reading

Paystack Transfers and Payouts: Complete Guide

Everything you need to send money out of your Paystack account: creating transfer recipients, initiating single and bulk transfers, handling OTP requirements, managing failures, and building automated payout systems for bank accounts, M-Pesa wallets, and mobile money across Africa.

Read more

Transfer OTP: Enabling, Disabling and Automating

Paystack requires OTP confirmation for transfers by default. This guide explains the OTP flow, how to disable it for automated payout systems, the security trade-offs of disabling OTP, and how to build your own application-level approval controls to replace OTP with something better suited to production automation.

Read more

Funding Your Paystack Balance Programmatically

Your Paystack balance needs funds before you can send transfers. This guide covers the different ways to fund your balance, setting up dedicated virtual accounts, monitoring balance levels, building automated funding alerts, and integrating balance management into your payout pipeline so you never run out mid-payout.

Read more

Building a Driver Payout System for a Ride Hailing App

Ride-hailing drivers need fast, reliable payouts. This guide covers building a driver payout system on Paystack: calculating trip-based earnings with commission deductions, daily settlement cycles, M-Pesa and bank payout options, handling cash-collected trips, real-time driver balance tracking, and instant withdrawal features.

Read more

Paystack Payment Verification and Reconciliation: Complete Guide

The complete engineering reference for verifying Paystack transactions server-side, preventing double grants, designing a payments ledger, reconciling settlements against your database, and building automated sweeper jobs that catch missing transactions.

Read more

Explore related topics

M-Pesa & African Stack

M-Pesa Daraja API tutorials, USSD development, and WhatsApp Business API guides. The African Stack that global bootcamps do not teach.

Learn to Code in Kenya

Everything a Kenyan needs to know before writing their first line of code. Career paths, real costs, hardware requirements, and honest answers about what it takes.

Learning to Code

Roadmaps, language guides, and practical advice for learning to code and launching a programming career.

Breaking Into Tech

Practical, no-nonsense guides for anyone looking to start or change their career in tech. From choosing your first language to acing interviews.

The African Stack: Skills for This Market

The skills that make Kenyan developers hard to replace: M-Pesa, USSD, WhatsApp, fintech, and building for the African market.

Bonaventure Ogeto

Bonaventure Ogeto

Founder, McTaba Labs

Bonaventure is the founder of McTaba Labs, a coding bootcamp in Nairobi, Kenya. He has spent years building products for the African market and mentoring the next generation of developers across the continent.