Payouts
Send money to mobile money wallets, bank accounts and Shika Wallets.
Payouts API
The Payouts API allows you to send money to mobile money wallets, bank accounts and Shika Wallets in Ghana. A Shika Wallet payout goes to a consumer's wallet or a cash agent's float, and is addressed by the recipient's Shika ID (for example SHK12345), their phone number, or their account ID.
The Payout Object
{
"id": "po_abc123def456",
"object": "payout",
"amount": 100,
"currency": "GHS",
"fee": 1.50,
"status": "completed",
"destination": {
"type": "mobile_money",
"provider": "mtn",
"number": "024****567",
"name": "John Doe"
},
"description": "Salary payment",
"metadata": {
"employee_id": "123"
},
"provider_reference": "OP123456789",
"failure_reason": null,
"created_at": "2024-01-15T10:00:00Z",
"completed_at": "2024-01-15T10:00:05Z"
}Attributes
| Attribute | Type | Description |
|---|---|---|
id | string | Unique identifier |
object | string | Always "payout" |
amount | number | Amount in GHS |
currency | string | Currency code (GHS) |
fee | number | Transaction fee in GHS |
status | string | created, pending, processing, succeeded, failed, cancelled |
destination | object | Destination account details |
destination.type | string | mobile_money, bank_transfer or shika_wallet |
destination.provider | string | Provider (e.g., mtn, telecel, airteltigo). Absent for shika_wallet |
destination.number | string | Masked account/phone number. For shika_wallet, the recipient's account ID |
destination.name | string | Recipient name |
destination.shika_id | string | shika_wallet only. The recipient's Shika ID |
destination.account_id | string | shika_wallet only. The recipient's ID (acc_... for a consumer, cag_... for a cash agent) |
destination.recipient_type | string | shika_wallet only. consumer or cash_agent |
description | string | Payout description |
metadata | object | Custom metadata |
provider_reference | string | Provider transaction reference |
failure_reason | string | Error message if failed |
created_at | string | Creation timestamp |
completed_at | string | Completion timestamp |
Create a Payout
Sends money to a mobile money wallet, bank account or Shika Wallet.
POST /v1/payoutsRequest Body
| Parameter | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Amount in GHS (e.g., 100 for GHS 100.00) |
currency | string | No | Currency (default: GHS) |
destination | object | Yes | Destination account details |
destination.type | string | Yes | mobile_money, bank_transfer or shika_wallet |
destination.phone_number | string | Yes* | Phone number (for mobile money, or to find a Shika Wallet) |
destination.provider | string | No | Provider code (auto-detected from phone): mtn, telecel, airteltigo |
destination.account_number | string | Yes* | Account number (for bank transfer) |
destination.bank_code | string | Yes* | Bank code (for bank transfer) |
destination.account_name | string | No | Recipient name |
destination.shika_id | string | Yes* | Recipient's Shika ID, e.g. SHK12345 (for Shika Wallet) |
destination.account_id | string | Yes* | Recipient's ID, acc_... for a consumer or cag_... for a cash agent (for Shika Wallet) |
otp_code | string | Yes | 6-digit OTP code for verification |
description | string | No | Payout description |
metadata | object | No | Custom metadata |
Before initiating a payout, you must first request an OTP using the OTP API. The OTP is sent to the merchant's registered phone number and must be verified within 10 minutes.
Mobile Money Payout
curl -X POST https://api.shikacreators.com/v1/payouts \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"amount": 100,
"currency": "GHS",
"destination": {
"type": "mobile_money",
"phone_number": "0241234567",
"account_name": "John Doe"
},
"otp_code": "123456",
"description": "Salary payment",
"metadata": {
"employee_id": "123"
}
}'const payout = await shikacreators.payouts.create({
amount: 100,
currency: 'GHS',
destination: {
type: 'mobile_money',
phone_number: '0241234567',
account_name: 'John Doe'
},
otp_code: '123456',
description: 'Salary payment',
metadata: {
employee_id: '123'
}
})payout = client.payouts.create(
amount=100,
currency='GHS',
destination={
'type': 'mobile_money',
'phone_number': '0241234567',
'account_name': 'John Doe'
},
otp_code='123456',
description='Salary payment',
metadata={'employee_id': '123'}
)$payout = $shikacreators->payouts->create([
'amount' => 100,
'currency' => 'GHS',
'destination' => [
'type' => 'mobile_money',
'phone_number' => '0241234567',
'account_name' => 'John Doe'
],
'otp_code' => '123456',
'description' => 'Salary payment',
'metadata' => ['employee_id' => '123']
]);Bank Transfer Payout
curl -X POST https://api.shikacreators.com/v1/payouts \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"amount": 500,
"currency": "GHS",
"destination": {
"type": "bank_transfer",
"account_number": "1234567890",
"bank_code": "030100",
"account_name": "John Doe"
},
"otp_code": "123456",
"description": "Vendor payment"
}'const payout = await shikacreators.payouts.create({
amount: 500,
currency: 'GHS',
destination: {
type: 'bank_transfer',
account_number: '1234567890',
bank_code: '030100',
account_name: 'John Doe'
},
otp_code: '123456',
description: 'Vendor payment'
})payout = client.payouts.create(
amount=500,
currency='GHS',
destination={
'type': 'bank_transfer',
'account_number': '1234567890',
'bank_code': '030100',
'account_name': 'John Doe'
},
otp_code='123456',
description='Vendor payment'
)Shika Wallet Payout
Send money straight to a Shika Wallet using the recipient's Shika ID. The recipient can be a consumer (paid into their wallet) or a cash agent (paid into their float). This is instant: no external provider is involved, and the payout comes back with status completed.
Give exactly one of shika_id, account_id or phone_number. When more than one is given, shika_id wins. You can confirm who you are paying first with POST /v1/lookup/shika-id.
curl -X POST https://api.shikacreators.com/v1/payouts \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"amount": 250,
"currency": "GHS",
"destination": {
"type": "shika_wallet",
"shika_id": "SHK12345"
},
"otp_code": "123456",
"description": "Prize payment"
}'const payout = await shikacreators.payouts.create({
amount: 250,
currency: 'GHS',
destination: {
type: 'shika_wallet',
shika_id: 'SHK12345'
},
otp_code: '123456',
description: 'Prize payment'
})payout = client.payouts.create(
amount=250,
currency='GHS',
destination={
'type': 'shika_wallet',
'shika_id': 'SHK12345'
},
otp_code='123456',
description='Prize payment'
)$payout = $shikacreators->payouts->create([
'amount' => 250,
'currency' => 'GHS',
'destination' => [
'type' => 'shika_wallet',
'shika_id' => 'SHK12345'
],
'otp_code' => '123456',
'description' => 'Prize payment'
]);The response carries the recipient's Shika ID, ID and type in destination. For a cash agent, account_id is a cag_... id and recipient_type is cash_agent:
{
"id": "po_abc123def456",
"object": "payout",
"amount": 250,
"currency": "GHS",
"fee": 0,
"status": "completed",
"destination": {
"type": "shika_wallet",
"number": "acc_abc123def456",
"name": "Ama Mensah",
"shika_id": "SHK12345",
"account_id": "acc_abc123def456",
"recipient_type": "consumer"
},
"description": "Prize payment",
"provider_reference": null,
"failure_reason": null,
"created_at": "2024-01-15T10:00:00Z",
"completed_at": "2024-01-15T10:00:00Z"
}A Shika ID is unique across consumers, cash agents and merchants. Consumers and cash agents can be paid. If the ID belongs to a merchant, the request fails with shika_id_not_payable. A sub-agent cannot be paid directly; pay the main agent instead (subagent_not_payable).
The Disbursements API accepts the same shika_wallet destination without an OTP, for automated server-to-server payments.
Supported Providers
Mobile Money
| Provider | Code | Phone Prefixes |
|---|---|---|
| MTN Mobile Money | mtn | 024, 025, 053, 054, 055, 059 |
| Telecel Cash | telecel | 020, 050 |
| AirtelTigo Money | airteltigo | 026, 027, 056, 057 |
The provider is automatically detected from the phone number. You can optionally specify it explicitly.
Banks
Fetch the bank list with GET /v1/lookup/banks and use the bank_code it returns.
curl https://api.shikacreators.com/v1/lookup/banks \
-H "Authorization: Bearer sk_live_..."Do not hardcode bank codes. Your account is routed to one of several banking
partners, and each partner identifies banks with its own codes. GET /v1/lookup/banks
always returns the codes valid for your account. A code from anywhere else is
rejected, so cache the list rather than copying values into your source.
Response
{
"id": "po_abc123def456",
"object": "payout",
"amount": 100,
"currency": "GHS",
"fee": 1.50,
"status": "completed",
"destination": {
"type": "mobile_money",
"provider": "mtn",
"number": "024****567",
"name": "John Doe"
},
"description": "Salary payment",
"metadata": {
"employee_id": "123"
},
"provider_reference": "OP123456789",
"failure_reason": null,
"created_at": "2024-01-15T10:00:00Z",
"completed_at": "2024-01-15T10:00:05Z"
}Retrieve a Payout
Retrieves a payout by ID.
GET /v1/payouts/:idcurl https://api.shikacreators.com/v1/payouts/po_abc123def456 \
-H "Authorization: Bearer sk_test_..."const payout = await shikacreators.payouts.retrieve('po_abc123def456')payout = client.payouts.retrieve('po_abc123def456')List Payouts
Returns a list of payouts.
GET /v1/payoutsQuery Parameters
| Parameter | Type | Description |
|---|---|---|
limit | integer | Number of results (1-100) |
starting_after | string | Cursor for pagination |
status | string | Filter by status |
curl "https://api.shikacreators.com/v1/payouts?status=succeeded&limit=10" \
-H "Authorization: Bearer sk_test_..."const payouts = await shikacreators.payouts.list({
status: 'succeeded',
limit: 10
})payouts = client.payouts.list(status='succeeded', limit=10)Response
{
"object": "list",
"data": [
{
"id": "po_abc123def456",
"object": "payout",
"amount": 100,
"status": "succeeded",
...
}
],
"has_more": true,
"url": "/v1/payouts"
}Payout Statuses
| Status | Description |
|---|---|
created | Payout has been created |
pending | Payout is queued for processing |
processing | Payout is being processed by the provider |
succeeded | Payout was successful |
failed | Payout failed |
cancelled | Payout was cancelled |
Error Codes
| Code | Description |
|---|---|
insufficient_balance | Not enough balance in your account |
invalid_destination | Destination account is invalid |
recipient_unavailable | Recipient's account is unavailable |
daily_limit_exceeded | Daily payout limit exceeded |
amount_too_small | Amount below minimum (GHS 1.00) |
amount_too_large | Amount above maximum |
otp_verification_required | OTP verification is required |
invalid_phone_number | Invalid Ghana phone number |
invalid_shika_id | shika_id is not in the form SHK12345 |
shika_id_not_found | No consumer or cash agent has that Shika ID |
shika_id_not_payable | The Shika ID belongs to a merchant, which cannot be paid this way |
subagent_not_payable | The Shika ID belongs to a sub-agent. Pay the main agent instead |
account_not_found | No active consumer or cash agent matches the account_id or phone_number |
wallet_not_active | The recipient's wallet or float is frozen or suspended |
Webhook Events
| Event | Description |
|---|---|
payout.created | Payout was created |
payout.pending | Payout is pending processing |
payout.completed | Payout was successful |
payout.failed | Payout failed |
app.post('/webhooks', (req, res) => {
const event = Webhook.constructEvent(req.body, signature, secret)
switch (event.type) {
case 'payout.completed':
const payout = event.data.object
console.log(`Payout ${payout.id} completed!`)
break
case 'payout.failed':
const failedPayout = event.data.object
console.log(`Payout failed: ${failedPayout.failure_reason}`)
break
}
res.json({ received: true })
})OTP Verification Flow
Payouts require OTP verification for security. Here's the complete flow:
Step 1: Request OTP
curl -X POST https://api.shikacreators.com/v1/otp/request \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"purpose": "PAYOUT"
}'Step 2: Verify OTP
curl -X POST https://api.shikacreators.com/v1/otp/verify \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"code": "123456",
"purpose": "PAYOUT"
}'Step 3: Create Payout
Use the verified OTP code when creating the payout:
curl -X POST https://api.shikacreators.com/v1/payouts \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"amount": 100,
"destination": {
"type": "mobile_money",
"phone_number": "0241234567"
},
"otp_code": "123456"
}'The OTP is valid for 10 minutes after verification. If expired, you'll need to request a new OTP.