Shopika rails · Built for Kenya

Sell airtime and bundles.
Earn 5%.
One Kenyan API.

Connect your app, SACCO, kiosk platform, or checkout to Safaricom, Airtel, Telkom, and Faiba. Fund with M-Pesa and keep every sale visible from request to delivery.

Kenya-first coverage No double charging Signed delivery updates
first-sale.sh
curl -X POST https://api.shopika.com/v1/airtime \
  -H "Authorization: Bearer sk_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "recipient": "0712345678",
    "amount": 50,
    "reference": "order-1042"
  }'

# Delivered synchronously
{ "success": true, "status": "DELIVERED" }
5%
More float every time you fundEarn 5% bonus float on every successful M-Pesa top-up.
Top up KES 1,000 → receive KES 1,050 float
Mobile network coverageOne integration across Kenya
SafaricomAirtime + bundles
AirtelAirtime + bundles
TelkomAirtime + bundles
FaibaAirtime support
How a Shopika sale moves

From your order to their phone.

The API separates payment, fulfilment, and final status so your product always knows what happened.

01

Fund your float

Send an M-Pesa STK push and receive an extra 5% bonus float.

02

Create the sale

Pass a Kenyan number and your own unique order reference.

03

Shopika fulfils

Airtime returns immediately; bundles enter the delivery queue.

04

Receive the outcome

Poll status or receive a signed delivered, failed, or refunded webhook.

Designed around real Kenyan payment flows: M-Pesa funding, local phone formats, prepaid float, and automatic refunds when delivery fails.

The Shopika API surface

Six endpoints. No maze.

Sell, fund, and reconcile without assembling separate network integrations.

POST/v1/airtime

Sell airtime instantly.

POST/v1/bundles

Sell data, SMS, or minutes.

GET/v1/bundles_list

Browse available bundles.

GET/v1/wallet

View balance and ledger.

POST/v1/wallet_topup

Top up float through M-Pesa — earns an instant bonus.

GET/v1/transactions

Track a sale to completion.

Quickstart

Authentication

Create an account to get a live API key instantly. Send it as a bearer token with every request. All endpoints use JSON.

Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Your key is shown only at signup or rotation. Store it securely—this key can spend your float.

POST

Sell airtime Synchronous

curl -X POST https://api.shopika.com/v1/airtime \
  -H "Authorization: Bearer sk_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "recipient": "0712345678",
    "amount": 50,
    "reference": "your-unique-order-id"
  }'

reference is your idempotency key. Retrying the same reference will never charge the float twice.

Response

{
  "success": true,
  "status": "DELIVERED",
  "transaction_id": 4821,
  "reference": "your-unique-order-id",
  "wallet_balance": 4850.00
}
POST

Sell a bundle Async · 202

curl -X POST https://api.shopika.com/v1/bundles \
  -H "Authorization: Bearer sk_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "recipient": "0712345678",
    "bundle_id": 17,
    "reference": "your-unique-order-id"
  }'
{
  "success": true,
  "status": "PENDING",
  "transaction_id": 4822,
  "reference": "your-unique-order-id",
  "wallet_balance": 4700.00
}

Bundle delivery runs asynchronously. Float is debited immediately; a failed delivery is refunded automatically and reported through your webhook.

GET

Check transaction status

curl "https://api.shopika.com/v1/transactions?transaction_id=4822" \
  -H "Authorization: Bearer sk_live_xxxx"
{
  "success": true,
  "transaction_id": 4822,
  "reference": "your-unique-order-id",
  "service_type": "bundle",
  "status": "DELIVERED",
  "recipient": "254712345678",
  "operator": "safaricom",
  "bundle_id": 17,
  "amount": 50.00,
  "failure_reason": null,
  "created_at": "2026-07-15 09:40:00",
  "updated_at": "2026-07-15 09:40:22"
}
POST

Top up your float

curl -X POST https://api.shopika.com/v1/wallet_topup \
  -H "Authorization: Bearer sk_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{"phone": "0712345678", "amount": 5000}'

This sends an M-Pesa STK push. Your float is credited once payment is confirmed, plus an instant top-up bonus on top — no per-sale discounting, the bonus lands the moment your deposit clears. Poll GET /v1/wallet to see the new balance and the bonus credit in your ledger.

Stay in sync

Webhooks

Shopika sends JSON to your configured URL when a sale becomes DELIVERED, REFUNDED, or FAILED. Failed webhook deliveries are retried with backoff.

{
  "event": "transaction.delivered",
  "transaction_id": 4822,
  "reference": "your-unique-order-id",
  "service_type": "bundle",
  "status": "DELIVERED",
  "recipient": "254712345678",
  "operator": "safaricom",
  "bundle_id": 17,
  "amount": 50.00,
  "failure_reason": null
}

Verify the signature

Every request includes an X-Shopika-Signature header. Verify it before trusting the payload.

$signature = hash_hmac(
    'sha256',
    file_get_contents('php://input'),
    $yourWebhookSecret
);

if (!hash_equals($signature, $_SERVER['HTTP_X_SHOPIKA_SIGNATURE'])) {
    http_response_code(401);
    exit;
}
Predictable failures

Errors

Non-2xx responses include success: false, an error_code, and a readable message. Common codes include UNAUTHORIZED, INSUFFICIENT_FLOAT, INVALID_RECIPIENT, UNSUPPORTED_NETWORK, BUNDLE_NOT_FOUND, RATE_LIMITED, and DELIVERY_FAILED.