Skip to main content
Customer wallets are prepaid balances that Monigo can debit at billing time (see Prepaid Billing) or that you can charge programmatically for any purpose. Every wallet operation is recorded with double-entry ledger entries for full auditability.

Core concepts

ConceptDescription
WalletA balance in a single currency belonging to one customer. A customer can have multiple wallets (one per currency).
Ledger entryOne side of a double-entry accounting record. Every credit/debit creates exactly two entries — one debit and one credit — to keep books balanced.
Virtual accountA dedicated bank account (via Paystack, Flutterwave, or Monnify) that automatically credits the wallet when funds are deposited.

Creating a wallet

Use getOrCreate to ensure a wallet exists for a customer + currency pair. If one already exists, it is returned; otherwise a new wallet with zero balance is created.
curl -X POST https://api.monigo.co/v1/wallets \
  -H "Authorization: Bearer mk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "cust_uuid",
    "currency": "NGN"
  }'

Listing wallets

All wallets

The org is inferred from your API key.
resp, err := client.Wallets.List(ctx)
for _, w := range resp.Wallets {
    fmt.Printf("  %s%s %s\n", w.CustomerID, w.Balance, w.Currency)
}

Filter by customer

You can pass an optional customer_id query parameter to filter wallets:
resp, err := client.Wallets.List(ctx, monigo.ListWalletsParams{
    CustomerID: "cust_uuid",
})
Or use the dedicated customer wallets endpoint:
resp, err := client.Wallets.ListByCustomer(ctx, "cust_uuid")

Getting a wallet

Fetching a single wallet also returns its virtual accounts.
resp, err := client.Wallets.Get(ctx, "wallet_uuid")
fmt.Printf("Balance: %s\n", resp.Wallet.Balance)
fmt.Printf("Virtual accounts: %d\n", len(resp.VirtualAccounts))

Crediting a wallet

Credit adds funds to the wallet. Every credit creates two ledger entries (debit provider, credit wallet) and fires a customer.wallet.topped_up webhook.
curl -X POST https://api.monigo.co/v1/wallets/{wallet_id}/credit \
  -H "Authorization: Bearer mk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "10000.00",
    "currency": "NGN",
    "description": "Top-up via payment link",
    "entry_type": "deposit",
    "reference_type": "payment_link",
    "reference_id": "pay_abc123",
    "idempotency_key": "topup_abc123"
  }'
Always provide an idempotency_key when crediting wallets. This prevents double-credits if the request is retried. Use a stable reference from your own system (payment ID, transfer reference, etc.).

Entry types for credits

ConstantValueUse case
WalletEntryTypeDeposit / WalletEntryType.DepositdepositCustomer top-up, payment received
WalletEntryTypeRefund / WalletEntryType.RefundrefundReversing a previous charge
WalletEntryTypeAdjustment / WalletEntryType.AdjustmentadjustmentManual balance correction

Debiting a wallet

Debit removes funds from the wallet. Returns a 402 Payment Required error if the balance is insufficient.
resp, err := client.Wallets.Debit(ctx, walletID, monigo.DebitWalletRequest{
    Amount:         "500.00",
    Currency:       "NGN",
    Description:    "Usage charge for March 2026",
    EntryType:      monigo.WalletEntryTypeUsage,
    ReferenceType:  "invoice",
    ReferenceID:    "inv_abc123",
    IdempotencyKey: "debit_inv_abc123",
})
if monigo.IsQuotaExceeded(err) {
    // 402 — insufficient balance
    log.Println("Wallet balance too low")
}

Entry types for debits

ConstantValueUse case
WalletEntryTypeUsage / WalletEntryType.UsageusageMetered usage charge
WalletEntryTypeWithdrawal / WalletEntryType.WithdrawalwithdrawalCustomer withdrawal
WalletEntryTypeAdjustment / WalletEntryType.AdjustmentadjustmentManual balance correction

Transaction history

List paginated ledger entries for a wallet.
resp, err := client.Wallets.ListTransactions(ctx, walletID, monigo.ListTransactionsParams{
    Limit:  25,
    Offset: 0,
})
for _, tx := range resp.Transactions {
    fmt.Printf("  %s %s %s%s (%s%s)\n",
        tx.Direction, tx.Amount, tx.Currency,
        tx.Description, tx.BalanceBefore, tx.BalanceAfter)
}

Virtual accounts

Virtual accounts are dedicated bank accounts (via Paystack, Flutterwave, or Monnify) that automatically credit the wallet when a customer deposits funds. This enables self-service top-ups without requiring API calls from your backend.

Create a virtual account

va, err := client.Wallets.CreateVirtualAccount(ctx, walletID, monigo.CreateVirtualAccountRequest{
    Provider: monigo.VirtualAccountProviderPaystack,
    Currency: "NGN",
})
fmt.Printf("Account: %s at %s (%s)\n", va.AccountNumber, va.BankName, va.AccountName)

List virtual accounts

resp, err := client.Wallets.ListVirtualAccounts(ctx, walletID)
for _, va := range resp.VirtualAccounts {
    fmt.Printf("  %s%s %s (%s)\n", va.Provider, va.AccountNumber, va.BankName, va.Currency)
}
Provider constantValue
VirtualAccountProviderPaystack / VirtualAccountProvider.Paystackpaystack
VirtualAccountProviderFlutterwave / VirtualAccountProvider.Flutterwaveflutterwave
VirtualAccountProviderMonnify / VirtualAccountProvider.Monnifymonnify

Prepaid billing integration

Wallets are the foundation of Prepaid Billing. When a customer subscribes to a prepaid plan:
  1. Monigo auto-creates a wallet in the plan’s currency
  2. At period-end, Monigo debits the wallet atomically and creates a paid invoice
  3. If the balance is insufficient, the subscription is paused and a subscription.prepaid_balance_insufficient webhook fires
  4. When you credit the wallet and the balance covers the outstanding invoice, Monigo auto-resumes the subscription
This means you only need to handle wallet top-ups — Monigo takes care of billing, pausing, and resuming automatically.

Webhook events

EventWhen fired
customer.wallet.topped_upWallet credited successfully
subscription.prepaid_balance_insufficientPrepaid billing failed — wallet balance too low
invoice.paidPrepaid invoice paid via wallet debit