Escrow Safe Trade is live: protect your next high-value deal Learn more
ProductsPayment links & checkoutM-Pesa, cards, invoices, split payoutsEscrow Safe TradeMilestone and release-based escrowWallets & P2PPersonal and business walletsChama, Lengo & DeniGroup savings, goals and IOUsBusiness toolkitAccounting, payroll, POS, ticketingBulk payments & payrollPay many people in one uploadSubscriptions & billingRecurring plans and renewalsRent, loans & donationsCollections and campaignsAll products
SolutionsMerchantsBuyersChama groupsBusiness solutionsIndustries
Pricing
DevelopersDeveloper overviewQuickstart, auth and sandboxREST APIsCheckout, invoices and requestsWebhooksSigned, real-time eventsWallet-as-a-ServiceVirtual sub-accountsDocumentationGuides and API reference
Security
CompanyAbout usCareersPartnersBlogHelp centreContact us
Log in Get started Talk to sales
Get started

Quickstart: take your first payment

Go from nothing to a paid checkout session in six steps. You need a Paysasa business, an API key and a server that can make an HTTPS request.

Base URLhttps://api.paysasa.com/api/v1Authx-api-keyFormatJSON
1

Get your business verified#

Money movement is switched on per business. Create your business in the dashboard, complete KYC and wait for verification. Until then, create and pay calls answer 403 with business_unverified or owner_kyc_unverified. Details are on Compliance & trust.

2

Create an API key#

In the dashboard open Businesses → API Keys and generate a key. It looks like apk_live_xxx.secret (live) or apk_test_xxx.secret (test). Store it in an environment variable on your server, never in a browser bundle or a public repository. See Authentication.

3

Create a checkout session#

Post the amount in minor units (KES 150.00 is 15000), a currency, your business ID and the URLs customers return to. The response contains a hosted_url.

cURL
curl -X POST "https://api.paysasa.com/api/v1/checkout/sessions" \
-H "x-api-key: apk_test_xxxxxxxx.your_secret" \
-H "Content-Type: application/json" \
-d '{
  "amount": 15000,
  "currency": "KES",
  "business_id": "biz_xxxxxxxxxxxxxxxx",
  "description": "Order #1042",
  "success_url": "https://yourstore.com/order/success?id=1042",
  "cancel_url": "https://yourstore.com/order/cancel?id=1042"
}'
JavaScript
// Create a checkout session (Node.js)
const res = await fetch("https://api.paysasa.com/api/v1/checkout/sessions", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-api-key": "apk_live_a1b2c3d4.7f9e0a...",
  },
  body: JSON.stringify({
    amount: 15000,            // KES 150.00
    currency: "KES",
    business_id: "biz_xxxxxxxxxxxxxxxx",
    description: "Order #1042 — 2x T-Shirts",
    success_url: "https://yourstore.com/order/success?id=1042",
    cancel_url: "https://yourstore.com/order/cancel?id=1042",
    return_url: "https://yourstore.com/checkout/return?id=1042",
    allow_methods: ["wallet", "m_pesa"],
    metadata: { orderId: "1042", customerId: "cus_abc" },
    // Optional: percentage split across two same-business wallets
    use_split: true,
    split_mode: "percentage",
    split_recipients: [
      { wallet_id: "wal_fulfillment_01", percentage: 60 },
      { wallet_id: "wal_marketing_01", percentage: 40 },
    ],
    // Or fixed amounts (minor units) that sum to amount:
    // use_split: true,
    // split_mode: "fixed",
    // split_recipients: [
    //   { wallet_id: "wal_ops_01", amount: 10000 },
    //   { wallet_id: "wal_reserve_01", amount: 5000 },
    // ],
  }),
});

const { session } = await res.json();
// Redirect customer:
window.location.href = session.hosted_url;

A successful response contains the new session:

Response · JSON
// Response 201 Created
{
  "session": {
    "id": "ses_01hx...",
    "reference": "CHK-20240815-0042",
    "status": "pending",
    "amount": 15000,
    "currency": "KES",
    "hosted_url": "https://app.paysasa.com/checkout/ses_01hx...",
    "expires_at": "2024-08-15T15:00:00.000Z",
    "metadata": { "orderId": "1042" },
    "return_url": "https://yourstore.com/checkout/return?id=1042",
    "success_url": "https://yourstore.com/order/success?id=1042",
    "cancel_url": "https://yourstore.com/order/cancel?id=1042"
  }
}
4

Send the customer to pay#

Redirect the customer to session.hosted_url. Paysasa shows the payment methods you allowed (M-Pesa, wallet, card where enabled), then sends them back to your success_url or cancel_url. Do not trust the redirect alone: confirm the result from your server by polling GET /checkout/sessions/:id or, better, from a webhook.

5

Listen for webhooks#

Add a webhook URL in your business settings and handle checkout.paid to fulfil orders. Every delivery is signed with HMAC-SHA256 in the x-paysasa-signature header, so verify the raw body before trusting it:

JavaScript
import { createHmac } from "crypto";

function verifyWebhook(rawBody: string, signature: string, secret: string): boolean {
  const expected = createHmac("sha256", secret)
  .update(rawBody)
  .digest("hex");
  // Use timingSafeEqual to prevent timing attacks
  const a = Buffer.from(expected, "hex");
  const b = Buffer.from(signature.replace("sha256=", ""), "hex");
  if (a.length !== b.length) return false;
  return require("crypto").timingSafeEqual(a, b);
}

// In your webhook handler (e.g. Next.js Route Handler):
export async function POST(req: Request) {
  const rawBody = await req.text();
  const sig = req.headers.get("x-paysasa-signature") ?? "";
  if (!verifyWebhook(rawBody, sig, process.env.WEBHOOK_SECRET!)) {
    return Response.json({ error: "Invalid signature" }, { status: 401 });
  }
  const event = JSON.parse(rawBody);
  // Process event.event ...
  return Response.json({ received: true });
}

The full event list and payload shape are on Webhooks.

6

Harden and go live#

  • Send an idempotency key on payments so a retry never charges twice.
  • Branch on the HTTP status and, for 403s, the machine code. See Error codes.
  • Switch from apk_test_ to apk_live_ keys when your business is verified.
  • Browse every endpoint for payouts, invoices, subscriptions, Lengo, WaaS and more.