Paystack transfers
Paystack Transfers API — send money to bank accounts and mobile wallets. Initiate single and bulk transfers, finalize OTP-verified transfers, list, fetch, and verify transfer status. Use this skill whenever implementing payouts, disbursements, vendor payments, withdrawal flows, or any feature that sends money from your Paystack balance to recipients. Also use when you see references to transfer_code, TRF_ prefixed codes, the /transfer endpoint, or need to handle transfer OTP verification.From its SKILL.md
npx -y skills add rexedge/paystack --skill paystack-transfersAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
6.2 KB, ~1.5k tokens by cl100k_base, as published. Nobody here has run it
Paystack Transfers
The Transfers API lets you send money from your Paystack balance to bank accounts and mobile wallets. Transfers require a pre-created transfer recipient.
Depends on: paystack-setup for the
paystackRequesthelper.
Related: paystack-transfer-recipients for creating recipients before transfers.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /transfer | Initiate a transfer |
| POST | /transfer/finalize_transfer | Finalize transfer (OTP) |
| POST | /transfer/bulk | Initiate bulk transfer |
| GET | /transfer | List transfers |
| GET | /transfer/:id_or_code | Fetch a transfer |
| GET | /transfer/verify/:reference | Verify a transfer |
Initiate Transfer
POST /transfer
Returns status pending if OTP is disabled on your account, or otp if OTP is required.
| Param | Type | Required | Description |
|---|---|---|---|
source | string | Yes | balance (only option) |
amount | integer | Yes | Amount in subunits (kobo/pesewas) |
recipient | string | Yes | Recipient code (e.g. RCP_gd9vgag7n5lr5ix) |
reference | string | Yes | Unique identifier (16-50 chars, lowercase a-z, 0-9, -, _) |
reason | string | No | Reason for transfer (shows in customer's credit notification) |
currency | string | No | Currency code (defaults to NGN) |
account_reference | string | No | Required in Kenya for MPESA Paybill and Till transfers |
const transfer = await paystackRequest<{
transfer_code: string;
status: string;
reference: string;
amount: number;
}>("/transfer", {
method: "POST",
body: JSON.stringify({
source: "balance",
amount: 100000, // ₦1,000
recipient: "RCP_gd9vgag7n5lr5ix",
reference: `txf_${crypto.randomUUID()}`,
reason: "Vendor payment for January",
}),
});
// transfer.data.status → "pending" or "otp"
// transfer.data.transfer_code → "TRF_v5tip3zx8nna9o78"
Finalize Transfer
POST /transfer/finalize_transfer
Required when transfer status is otp. Submit the OTP sent to your business phone.
| Param | Type | Required | Description |
|---|---|---|---|
transfer_code | string | Yes | Transfer code from initiate response |
otp | string | Yes | OTP sent to business phone |
await paystackRequest("/transfer/finalize_transfer", {
method: "POST",
body: JSON.stringify({
transfer_code: "TRF_v5tip3zx8nna9o78",
otp: "928783",
}),
});
Initiate Bulk Transfer
POST /transfer/bulk
Batch multiple transfers in a single request. OTP must be disabled on your account to use this endpoint.
| Param | Type | Required | Description |
|---|---|---|---|
source | string | Yes | balance |
transfers | array | Yes | Array of transfer objects |
Each transfer object:
| Param | Type | Required | Description |
|---|---|---|---|
amount | integer | Yes | Amount in subunits |
recipient | string | Yes | Recipient code |
reference | string | Yes | Unique reference (16-50 chars) |
reason | string | No | Transfer reason |
const result = await paystackRequest("/transfer/bulk", {
method: "POST",
body: JSON.stringify({
source: "balance",
currency: "NGN",
transfers: [
{
amount: 20000,
recipient: "RCP_gd9vgag7n5lr5ix",
reference: `txf_${crypto.randomUUID()}`,
reason: "Bonus for the week",
},
{
amount: 35000,
recipient: "RCP_m7ljkv8leesep7p",
reference: `txf_${crypto.randomUUID()}`,
reason: "January salary",
},
],
}),
});
// result.message → "2 transfers queued."
List Transfers
GET /transfer
| Param | Type | Required | Description |
|---|---|---|---|
perPage | integer | No | Records per page (default: 50) |
page | integer | No | Page number (default: 1) |
recipient | integer | No | Filter by recipient ID |
from | datetime | No | Start date |
to | datetime | No | End date |
const transfers = await paystackRequest("/transfer?perPage=20&page=1");
Fetch Transfer
GET /transfer/:id_or_code
const transfer = await paystackRequest(
`/transfer/${encodeURIComponent("TRF_v5tip3zx8nna9o78")}`
);
Verify Transfer
GET /transfer/verify/:reference
Verify the status of a transfer using its reference.
const result = await paystackRequest(
`/transfer/verify/${encodeURIComponent(reference)}`
);
// result.data.status → "success" | "failed" | "pending" | "reversed"
Transfer Flow
// 1. Create recipient (see paystack-transfer-recipients skill)
// 2. Initiate transfer
const transfer = await paystackRequest("/transfer", {
method: "POST",
body: JSON.stringify({
source: "balance",
amount: 500000,
recipient: "RCP_gd9vgag7n5lr5ix",
reference: `txf_${crypto.randomUUID()}`,
}),
});
// 3. If OTP required, finalize
if (transfer.data.status === "otp") {
const otp = await getOTPFromUser();
await paystackRequest("/transfer/finalize_transfer", {
method: "POST",
body: JSON.stringify({
transfer_code: transfer.data.transfer_code,
otp,
}),
});
}
// 4. Listen for webhooks: transfer.success, transfer.failed, transfer.reversed
Important Notes
- You can only transfer from
balance— ensure your Paystack balance has sufficient funds - Bulk transfers require OTP to be disabled (Dashboard → Settings → Preferences)
- Transfer references must be unique, 16-50 characters, lowercase with
-and_only - For Kenya MPESA Paybill/Till, include
account_referencein the transfer - Always verify transfer status via webhook (
transfer.success,transfer.failed) rather than polling
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 0 of the 12 instructions most quality gates skills give in ~1.5k tokens
Counted across 1,195 of the 2,094 authors here whose files we hold, read 2026-08-07
- Read the output and check the exit codein 54 of 1195, across 14 files
- Verify requirements using a line-by-line checklistin 53 of 1195, across 12 files
- Identify the verification command proving the claimin 51 of 1195, across 12 files
- Run the full verification commandin 50 of 1195, across 11 files
- Verify output confirms the claimin 49 of 1195, across 12 files
- Check version control diff after agent delegationin 46 of 1195, across 6 files
- State claim with evidencein 44 of 1195, across 4 files
- Run the test suitein 33 of 1195, across 26 files
- Keep state in memory by defaultin 27 of 1195, across 6 files
- Make prototype runnable with one commandin 26 of 1195, across 5 files
- Produce a verification reportin 25 of 1195, across 14 files
- Detect the package manager from lockfilesin 24 of 1195, across 5 files
Said here and by no other author read
- create a transfer recipient before initiating a transfer
- set source to balance
- provide amount in subunits
- finalize transfer with otp when required
- disable otp to use bulk transfers
- make transfer references unique and lowercase
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.