Skip to main content

Getting started

The Transactions API lets your company collect payments (M-Pesa STK push, hosted checkout with card/bank/M-Pesa, Pesalink), send payouts (M-Pesa B2C, Pochi la Biashara, Till, Paybill), and manage customers and their wallets — all through a single REST API, with signed webhooks for real-time updates.

Payment links

Create an invoice, then redirect or embed — get paid by any method.

Direct API calls

Push an STK prompt or a payout straight from your own UI.

Customers & wallets

Onboard a customer — their wallet is created automatically.

Webhooks

Get a signed HMAC event the moment a transaction settles.

Two ways to integrate​

Almost everything you'll build fits one of two shapes:

Flow 1 — Direct API callFlow 2 — Payment link
You already haveYour own payment UINo payment UI, or want one you don't maintain
You callPOST /transactions/.../initiate directlyPOST /checkout/invoices, then redirect or embed the hosted page
The customer seesWhatever you buildThe Taifa Pay hosted checkout (redirect, or embedded via iframe)
SDK support todayNone — call the HTTP API directly@taifa/payments-node (create/reconcile) + @taifa/payments-js (embed)

Both flows end the same way: you get back a pending transaction immediately, and the final outcome (COMPLETED / FAILED) arrives later — pushed via webhook or fetched with GET /transactions/{id}. Never treat the initiate/invoice response itself as the final result.

Base URL​

All API endpoints are versioned under the /v1 prefix:

EnvironmentBase URL
Sandboxhttps://sandbox.merchants.taifapay.africa/v1
Productionhttps://merchants.taifapay.africa/v1
note

Build and test your integration against sandbox first. Every endpoint in these guides behaves identically in both environments — only the host and your credentials change when you go live. The examples below use the sandbox host.

Onboarding​

Before you write any code, three one-time things happen:

  1. We create your merchant account. Our team provisions the company that owns all your transactions, customers, and credentials — you don't create this yourself.
  2. You register your webhook URL(s) and collect your webhook secret. From the portal, tell us where to POST transaction updates and copy the generated HMAC secret; you'll use it to verify deliveries (see Webhooks).
  3. You create client credentials. Generate a client_id / client_secret pair in the portal — one pair per environment (sandbox and production are separate). The secret is shown once; store it securely.

Once you have credentials, everything below is self-serve.

Prerequisites​

To call the API you need the client credentials from onboarding above — a client_id and client_secret for the environment you're targeting. Keep the secret server-side; never embed it in a mobile app or browser code.

0. Get an access token​

Both flows start the same way: exchange your credentials for a short-lived Bearer token (see Authentication for the full lifecycle). If you use the Node SDK (Flow 2, option A below) it does this for you automatically — skip ahead.

curl -X POST https://sandbox.merchants.taifapay.africa/v1/auth/token \
-u "your_client_id:your_client_secret" \
-H "Content-Type: application/json" \
-d '{"grant_type": "client_credentials"}'
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6...",
"expires_in": "3599",
"token_type": "Bearer"
}

Flow 1: Call the API directly​

Use this when you already have your own payment UI and just need Taifa Pay to move the money — you prompt the customer yourself and react to the result. There's no SDK for this path yet; call the HTTP API with your Bearer token.

Example — send an M-Pesa STK push (see Direct payments & payouts for B2C, Pochi, Till, Paybill and Pesalink):

curl -X POST https://sandbox.merchants.taifapay.africa/v1/transactions/m-pesa/c2b/initiate \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"phoneNumber": "0712345678",
"amount": 100,
"accountReference": "Account123",
"transactionDesc": "Top-up",
"externalId": "your-transaction-id"
}'

The customer enters their PIN on the prompt; the transaction resolves shortly after (see Track the payment below). Onboarding a customer instead? POST /customers creates their wallet in the same call — see Customers & wallets.

Use this when you want a ready-made payment page (method switcher, card form, bank instructions) without building it yourself. Two steps: create an invoice, then send the customer to pay — either by redirecting them, or by embedding the checkout directly in your own page.

Step 1: Create an invoice​

curl -X POST https://sandbox.merchants.taifapay.africa/v1/checkout/invoices \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"amount": 1500,
"accountReference": "INV-1001",
"description": "Order #1001 - 2x widget",
"customerPhone": "254712345678"
}'

The response includes a checkoutUrl for the customer, an invoiceNo (for embedding), and a transactionId for you:

{
"message": "Invoice created",
"invoice": {
"transactionId": "a3f1c2d4-5e6b-7a8c-9d0e-1f2a3b4c5d6e",
"checkoutUrl": "https://pay.taifapay.africa/pay/ABCINV-1001",
"invoiceNo": "ABCINV-1001",
"amount": 1500,
"currency": "KES",
"status": "open",
"methods": ["mpesa_stk", "card"],
"expiresAt": "2026-07-17T12:30:00.000Z"
}
}

:::tip The transactionId is stable transactionId settles whichever method the customer eventually picks (STK, card, bank, manual M-Pesa). Store it — it's the one id you track regardless of payment method. :::

:::caution Creating an invoice always needs your secret This step must happen server-side with your client_secret — the amount is fixed here, so it can never be set by the browser. Only the resulting public invoiceNo is safe to hand to the frontend. :::

Step 2: Send the customer to pay​

Pick one:

The simplest option — no extra dependency. Send the customer (SMS, email, WhatsApp, or an HTTP redirect from your site) to the checkoutUrl from step 1:

res.redirect(invoice.checkoutUrl);
// or just text/email invoice.checkoutUrl to the customer

The page is public and needs no authentication. It handles method selection, STK/card/bank collection, and shows its own status — you don't build any of that UI.

Track the payment​

Every money movement — invoice, direct STK, payout, wallet top-up — is a transaction with a stable id. Poll it any time:

curl https://sandbox.merchants.taifapay.africa/v1/transactions/a3f1c2d4-5e6b-7a8c-9d0e-1f2a3b4c5d6e \
-H "Authorization: Bearer $ACCESS_TOKEN"

A transaction moves through PENDING → COMPLETED (or FAILED).

:::tip Prefer webhooks Polling works, but a webhook pushes you the result the moment it's known — no busy-loop, no latency. The Node SDK ships constructWebhookEvent() / verifyWebhookSignature() to verify deliveries for you. Most integrations use webhooks as the source of truth and keep polling only as a reconciliation fallback (e.g. POST /webhooks/test to verify your endpoint, then rely on delivery). :::

Going live​

Once your flow works end-to-end against sandbox:

  1. Request production credentials (a separate client_id / client_secret — sandbox credentials don't work against production).
  2. Point your base URL (or environment: "production" in the SDK) at https://merchants.taifapay.africa/v1.
  3. If you're embedding checkout, switch the browser SDK's host to the production default (https://pay.taifapay.africa — just omit the option) and get your origin allow-listed for framing.
  4. Re-register your production webhook URL and rotate in its production webhook secret.
  5. Send a POST /webhooks/test against production to confirm delivery and signature verification before taking real traffic.

Where to go next​