Shika Creators

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

AttributeTypeDescription
idstringUnique identifier
objectstringAlways "payout"
amountnumberAmount in GHS
currencystringCurrency code (GHS)
feenumberTransaction fee in GHS
statusstringcreated, pending, processing, succeeded, failed, cancelled
destinationobjectDestination account details
destination.typestringmobile_money, bank_transfer or shika_wallet
destination.providerstringProvider (e.g., mtn, telecel, airteltigo). Absent for shika_wallet
destination.numberstringMasked account/phone number. For shika_wallet, the recipient's account ID
destination.namestringRecipient name
destination.shika_idstringshika_wallet only. The recipient's Shika ID
destination.account_idstringshika_wallet only. The recipient's ID (acc_... for a consumer, cag_... for a cash agent)
destination.recipient_typestringshika_wallet only. consumer or cash_agent
descriptionstringPayout description
metadataobjectCustom metadata
provider_referencestringProvider transaction reference
failure_reasonstringError message if failed
created_atstringCreation timestamp
completed_atstringCompletion timestamp

Create a Payout

Sends money to a mobile money wallet, bank account or Shika Wallet.

POST /v1/payouts

Request Body

ParameterTypeRequiredDescription
amountnumberYesAmount in GHS (e.g., 100 for GHS 100.00)
currencystringNoCurrency (default: GHS)
destinationobjectYesDestination account details
destination.typestringYesmobile_money, bank_transfer or shika_wallet
destination.phone_numberstringYes*Phone number (for mobile money, or to find a Shika Wallet)
destination.providerstringNoProvider code (auto-detected from phone): mtn, telecel, airteltigo
destination.account_numberstringYes*Account number (for bank transfer)
destination.bank_codestringYes*Bank code (for bank transfer)
destination.account_namestringNoRecipient name
destination.shika_idstringYes*Recipient's Shika ID, e.g. SHK12345 (for Shika Wallet)
destination.account_idstringYes*Recipient's ID, acc_... for a consumer or cag_... for a cash agent (for Shika Wallet)
otp_codestringYes6-digit OTP code for verification
descriptionstringNoPayout description
metadataobjectNoCustom 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

ProviderCodePhone Prefixes
MTN Mobile Moneymtn024, 025, 053, 054, 055, 059
Telecel Cashtelecel020, 050
AirtelTigo Moneyairteltigo026, 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/:id
curl 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/payouts

Query Parameters

ParameterTypeDescription
limitintegerNumber of results (1-100)
starting_afterstringCursor for pagination
statusstringFilter 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

StatusDescription
createdPayout has been created
pendingPayout is queued for processing
processingPayout is being processed by the provider
succeededPayout was successful
failedPayout failed
cancelledPayout was cancelled

Error Codes

CodeDescription
insufficient_balanceNot enough balance in your account
invalid_destinationDestination account is invalid
recipient_unavailableRecipient's account is unavailable
daily_limit_exceededDaily payout limit exceeded
amount_too_smallAmount below minimum (GHS 1.00)
amount_too_largeAmount above maximum
otp_verification_requiredOTP verification is required
invalid_phone_numberInvalid Ghana phone number
invalid_shika_idshika_id is not in the form SHK12345
shika_id_not_foundNo consumer or cash agent has that Shika ID
shika_id_not_payableThe Shika ID belongs to a merchant, which cannot be paid this way
subagent_not_payableThe Shika ID belongs to a sub-agent. Pay the main agent instead
account_not_foundNo active consumer or cash agent matches the account_id or phone_number
wallet_not_activeThe recipient's wallet or float is frozen or suspended

Webhook Events

EventDescription
payout.createdPayout was created
payout.pendingPayout is pending processing
payout.completedPayout was successful
payout.failedPayout 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.