Wallet transfers
Send money from your wallet to another iSmartPay business.
A wallet transfer moves money from your wallet to another iSmartPay business’s wallet. You find the business by its handle.
Handles
A handle is a business’s public name for receiving transfers. A business needs one to be paid. An owner claims it in the console, and only a verified business can claim one. A key cannot claim or change a handle.
The same handle works in the merchant console and the developer console.
Check the recipient
Look up the handle first, so you can show who you are about to pay:
GET /v1/recipients/acme-stores
{
"handle": "acme-stores",
"businessName": "Acme Stores",
"ownerName": "Ama Mensah",
"handleSince": "2026-08-14T09:30:00Z"
}
- The handle must match exactly.
businessNameis the handle’s display name, else the trading name, else the registered name. It can be missing when it is not known.- Your own handle is
400 own_handle. An unknown handle is404.
Send
POST /v1/transfers
Idempotency-Key: supplier-7731
{
"reference": "supplier-7731",
"handle": "acme-stores",
"amount": "25.00",
"currency": "GHS"
}
referenceis yours, and unique for each transfer.- Do not send a
pin. Unknown fields are refused. - Your own handle is
400 own_handle. An unknown handle is404.
This one request holds the money and confirms it. A 200 with status completed means the money moved:
{
"reference": "supplier-7731",
"amount": "25.00",
"currency": "GHS",
"status": "completed",
"recipientHandle": "acme-stores",
"recipientName": "Acme Stores",
"createdAt": "2026-10-05T10:00:00Z"
}
recipientName can be missing when it is not known. Other statuses, such as held, can appear.
Retrying
If a request fails, times out or returns 5xx, send the same reference and body again. The transfer carries on from where it stopped and is never sent twice. Use the same Idempotency-Key too.
Do not retry with a new reference. A new reference is a new transfer.
A reference already used for a different transfer is 409 reference_reused.
Fees and limits
Wallet transfers carry no fee. They count toward your outbound limits. Check GET /v1/limits before a large transfer.
Read a transfer
GET /v1/transfers/supplier-7731
This returns the transfer by your reference.
The receiving business
When a transfer completes, the recipient gets a transfer.received webhook:
{
"id": "evt_8d2c41f07a9e4b3c9d1e6f5a4b3c2d1e",
"type": "transfer.received",
"createdAt": "2026-10-05T12:00:00Z",
"data": {
"reference": "supplier-7731",
"amount": "25.00",
"currency": "GHS",
"senderName": "Acme Stores",
"completedAt": "2026-10-05T12:00:00Z"
}
}
referenceis the one the sender chose.senderNameis the sender’s trading name, else registered name. It can be missing when it is not known.- It is sent once per transfer, only when the money is in your wallet.
The transfer also shows in GET /v1/transactions, as a wallet_transfer with direction in. See Transactions and events.
Permissions
| To | The key needs |
|---|---|
| Look up a recipient or send | wallettransfer.write |
| Read a transfer | wallettransfer.read |
Webhooks
Subscribe an endpoint to these events in the console:
| Type | Sent when |
|---|---|
transfer.status_changed |
A transfer you sent moves to a new status, such as completed or failed. |
transfer.received |
Another business’s transfer to you completed. |
transfer.status_changed goes to the sending business only. data is the transfer as it stood when the event was sent. It does not carry the recipient’s handle or name. Fetch the transfer by reference when you need them.
If your webhook endpoints were set up before wallet transfers arrived, edit or add an endpoint in the console once. Until then, transfer events are not sent to your business. See Webhooks.
Errors
As well as the common errors:
| Status | Code | What to do |
|---|---|---|
| 400 | own_handle |
You cannot pay yourself. Check the handle. |
| 400 | transfer_refused |
The money could not be held. Nothing left your wallet. Read message. |
| 409 | reference_reused |
The reference belongs to another transfer. Use a new one. |
| 409 | transfer_not_held |
The hold can no longer be confirmed. Read the transfer to see where it stands. |
| 403 | transaction_limit_exceeded, daily_limit_exceeded, monthly_limit_exceeded |
The transfer is over your outbound limits. Check GET /v1/limits. |