Skip to main content

Send money

The Send Money API lets you pay out from your IntaSend wallet to M-Pesa, bank accounts, M-Pesa tills and paybills, cross-border mobile money, and other IntaSend accounts.

A payout request is a batch (a "file") containing one or more transactions. Every transaction in a batch uses the same provider, currency and country.

The flow​

initiate ──► (preview) ──► approve ──► balance check ──► sending ──► complete
│ ▲
└── requires_approval: "NO" skips the manual approve step
  1. Initiate a batch with POST /api/v1/send-money/initiate/.
  2. Approve it with POST /api/v1/send-money/approve/. You can skip this step with requires_approval: "NO".
  3. Some bank routes also need an OTP confirmation.
  4. Track the batch with status, or receive the result on your callback_url or a webhook.
Permissions

Initiating requires an API key (or user) with the Level-2 or Administrator role. Approving requires Level-3 or Administrator. With this split, one key can prepare payouts and a different key approves them.

1. Initiate​

curl -X POST https://api.intasend.com/api/v1/send-money/initiate/ \
-H "Authorization: Bearer $INTASEND_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"currency": "KES",
"provider": "MPESA-B2C",
"requires_approval": "YES",
"callback_url": "https://example.com/intasend/payouts",
"batch_reference": "payroll-2026-09",
"transactions": [
{"name": "Jane Doe", "account": "254712345678", "amount": "150", "narrative": "Salary"}
]
}'

Request fields​

FieldRequiredDescription
currencyYesWallet currency to pay from. One of KES, USD, EUR, GBP, GHS, NGN, UGX, TZS, XAF, XOF, RWF, CDF.
providerYesPayout rail. See Providers.
countryNoISO-2 destination country. Defaults to KE. It is required for cross-border payouts; see Countries.
wallet_idNoWallet to debit. It must match currency. Defaults to your settlement wallet for that currency. A working wallet must have can_disburse enabled.
requires_approvalNo"YES" (default) or "NO". With "NO", the batch is approved automatically.
callback_urlNoURL that receives the final batch result. See Callbacks.
batch_referenceNoYour own reference for the batch (max 70 characters).
transactionsYesList of transactions (below).

Transaction fields​

FieldRequiredDescription
accountYesThe destination: phone number, bank account, till, paybill or meter number. Digits only: no +, dashes or letters. Max 24 characters.
amountYesAmount to send, up to 2 decimal places.
nameDependsBeneficiary name. Required for bank transfers (RTGS/EFT, NG and UG banks). Letters, numbers, spaces, -, _ and : only.
narrativeNoPayment description or remarks. Same character rules as name.
bank_codeDependsRequired for bank payouts. See bank codes.
operator_codeDependsMobile network operator. Required in Tanzania (VODACOM, AIRTEL, TIGO, HALOTEL).
account_typeDependsTillNumber or PayBill. Required for MPESA-B2B.
account_referenceDependsPaybill account number. Required when account_type is PayBill. Max 20 characters.
notify_phoneDependsPhone number that receives the KPLC token. Required for KPLC.
category_nameNoName of a payout category you've set up, for reporting.
idempotency_keyNoUnique key per transaction. A repeat request with the same key is rejected, so you can retry safely.
Use idempotency keys

Set a unique idempotency_key on every transaction (for example your internal payout ID). If a network error makes you unsure whether a request went through, resend it: duplicates are rejected with duplicate transaction detected.

Response​

{
"file_id": "Y8Q2KLM",
"tracking_id": "8b0a3d0c-6f4e-4a8e-9a53-2f3c1b1f6e0d",
"batch_reference": "payroll-2026-09",
"status": "Preview and approve",
"status_code": "BP103",
"nonce": "a1b2c3",
"wallet": {"wallet_id": "XZ3LKQ", "currency": "KES", "current_balance": "12500.00", "available_balance": "12500.00"},
"transactions": [
{
"transaction_id": "KQ9PLXR",
"status": "Pending",
"status_code": "TP101",
"request_reference_id": "…",
"name": "Jane Doe",
"account": "254712345678",
"amount": "150.00",
"narrative": "Salary"
}
],
"charge_estimate": "10.00",
"total_amount_estimate": "160.00",
"total_amount": "150.00",
"transactions_count": 1
}

Keep the tracking_id: you need it to approve the batch and check its status.

2. Approve​

Send back the tracking_id, the nonce and the transactions array from the initiate response:

curl -X POST https://api.intasend.com/api/v1/send-money/approve/ \
-H "Authorization: Bearer $INTASEND_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"tracking_id": "8b0a3d0c-6f4e-4a8e-9a53-2f3c1b1f6e0d",
"nonce": "a1b2c3",
"transactions": [ /* the transactions array from initiate */ ]
}'

The batch moves to CHECKING-ACCOUNT-BALANCE (BP104). IntaSend then debits your wallet and sends each transaction. You can only approve a batch while it is in PREVIEW-AND-APPROVE (BP103).

3. Check status​

curl -X POST https://api.intasend.com/api/v1/send-money/status/ \
-H "Authorization: Bearer $INTASEND_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"tracking_id": "8b0a3d0c-6f4e-4a8e-9a53-2f3c1b1f6e0d"}'

The response adds actual_charges, paid_amount and failed_amount. Each transaction also gets provider, provider_reference (for example the M-Pesa receipt number), provider_account_name and charge. See Status codes for every batch and transaction state.

Cancel​

You can cancel a batch before it starts sending, while it is in BP103, BP104 or BF105 and every transaction is still pending:

curl -X POST https://api.intasend.com/api/v1/send-money/cancel/ \
-H "Authorization: Bearer $INTASEND_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"file_id": "Y8Q2KLM"}'

Callbacks​

If you set callback_url, IntaSend POSTs the full batch result (the same body as the status response) once the batch completes, fails on low balance or is cancelled.

The callback is sent once and is not retried, so don't rely on it alone. Also subscribe to the send_money_event webhook, which fires on every batch state change, includes your challenge for verification and can be replayed from the dashboard. Keep polling status as a fallback.

Refunds of failed transactions​

When a batch completes, the amount of any failed transaction, including its charge, is credited back to the wallet you paid from.