Quickstart
MojoWallet’s Python SDK supports Python 3.10 and later. Install the package, configure an API key, and retrieve a wallet.
1. Install
pip install mojowallet2. Configure
import mojowallet
mojowallet.configure(
"mvs-your-sandbox-key",
base_url="https://api.mojowallet.com",
)3. Make the first request
wallet = mojowallet.Wallet.get(42)
print(wallet.id, wallet.uuid, wallet.name)
print(wallet.balance("SC_REAL"))result.id) or dictionary syntax (result["id"]).Core concepts
Wallets and holdings
A wallet belongs to a customer. Its balance is the sum of holdings: balance buckets scoped by currency, category, spend priority, and cashability. This makes deposited, promotional, reserved, and other value independently auditable.
Currencies and families
Use exact currency codes such as SC_REAL for a single balance, or a root code such as SC to operate across an ordered currency family. Family spending walks configured priority and debits as many holdings as needed.
Integer amounts
All amounts use the smallest currency unit. For USD, 1050 represents $10.50. Loyalty or virtual units may already be whole units.
Reference IDs
Send a stable reference_id for financial operations. Safe retries return the original transaction instead of moving value twice. Reusing a reference with different data raises an explicit replay error.
Wallet operations
Operations are methods on a wallet instance. They map to the wallet action endpoint and return transaction or result objects.
| Method | Purpose |
|---|---|
| add_funds() | Credit value from an external source into a specific currency and holding category. |
| purchase() | Debit a currency for a merchant purchase after balance and spending-limit checks. |
| spend() | Debit across a root currency family in priority order with an idempotent reference. |
| cashout() | Debit cashable holdings only. The external payout is handled upstream. |
| transfer() | Move value to another wallet in the same tenant. |
| refund() | Fully or partially reverse a completed transaction in the same wallet. |
| redeem_promo() | Apply a promotional code exactly once per wallet and reference. |
| lock() / unlock() | Control wallet availability with an auditable reason. |
txn = wallet.add_funds(
5000,
"SC_REAL",
source="CREDIT_CARD",
category="deposited",
can_cashout=True,
reference_id="deposit-1042",
)
wallet.spend(
750,
root_code="SC",
reference_id="purchase-1042",
merchant="Partner Store",
)Balance reads
Choose the read that matches the question: exact currency, root family, per-holding detail, or a complete snapshot.
exact = wallet.balance("SC_REAL")
family = wallet.get_root_balance("SC")
holdings = wallet.get_holdings(root_code="SC")
summary = wallet.balance_summary()
summary.balances.SC.available
summary.balances.SC.by_code.SC_REAL
summary.total_by_currency.SC_REALbalance_summary().balances is keyed by root code. Per-currency totals remain under total_by_currency.Sessions
Withdrawal sessions provide concurrency control. Only one active withdrawal session may exist per wallet, preventing two flows from racing the same balance.
with wallet.session(
"game-round-2026",
expires_in_seconds=3600,
) as session:
session.withdraw(
500, "SC_REAL",
reference_id="bet-001",
)
session.extend(1800)
# The session closes even if an exception occurs.Reserve flow
External payouts are asynchronous. Reserve wallet value first, then confirm only when the processor succeeds—or release if it fails.
wallet.reserve(
2500,
"SC_REAL",
"SC_HOLD",
reference_id="payout-001",
)
# Processor succeeded
wallet.confirm_reservation("payout-001")
# Or return value when it failed
wallet.release_reservation("payout-001")If a hold expires before either terminal call, MojoWallet releases it automatically and emits a reservation-expired event.
REST API
Use the API directly from any server stack. Authenticate with a production mvp- or sandbox mvs- API key.
Authorization: apikey mvs-XXXXXXXXXXXXXXXX
Content-Type: application/json| Endpoint | Purpose |
|---|---|
| GET /api/wallet/wallet | List and filter wallets. |
| POST /api/wallet/wallet | Create a wallet for an existing customer. |
| POST /api/wallet/wallet/action/<pk> | Run financial and session actions. |
| POST /api/wallet/wallet/query/<pk> | Read balances, spending power, historical state, and metrics. |
| POST /api/wallet/wallet/batch | Read balances for multiple wallets. |
| GET /api/wallet/bonus_grant | Read wagering and promotional grant lifecycle. |
Read permission allows queries and listings. Manage permission adds wallet mutations. Admin grants full access.
Error handling
The SDK raises domain-specific exceptions for wallet failures. The numeric 5000-block code is authoritative even if an upstream HTTP status differs.
| Code | Exception | Meaning |
|---|---|---|
| 5001 | WalletInvariantError | A safety cross-check failed. Show a generic retry message. |
| 5002 | InsufficientBalanceError | Available balance is lower than required. |
| 5003 | WalletLockedError | The wallet is locked. |
| 5004 | WalletSuspendedError | The wallet is suspended, optionally until a known time. |
| 5005 | WalletInactiveError | The wallet is inactive. |
| 5006 | InvalidReferenceError | The reference is empty or malformed. |
| 5007 | IdempotentReplayError | The same reference was replayed with different input. |
from mojowallet.exceptions import (
InsufficientBalanceError,
SessionConflictError,
)
try:
wallet.cashout(
10000, "SC_REAL",
reference_id="cashout-002",
)
except InsufficientBalanceError as error:
print(error.available, error.required)
except SessionConflictError:
print("Another session is active")Need a human? Visit the Help center.