Wallets
A wallet is a ₦-denominated account tied to a user in your system. You identify your user with a userRef — your own user ID (UUID, database ID, or any string up to 255 characters). PayKore never stores your user's personal details, only the reference you provide.
Creating a wallet
curl -X POST https://api.paykore.dev/v1/wallets \
-H "X-API-Key: sk_test_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"userRef": "user_123",
"currency": "NGN",
"metadata": {"plan": "premium"}
}'
Request fields:
| Field | Required | Description |
|---|---|---|
userRef | Yes | Your user's ID. Max 255 characters. Must be unique per currency within your partner account. |
currency | Yes | Always "NGN" for now. |
metadata | No | Any JSON object. Returned as-is on every wallet response. PayKore does not read it. |
Response:
{
"id": "wlt_9f3kA2mXpQ",
"partner_id": "prt_X1y2Z3",
"user_ref": "user_123",
"currency": "NGN",
"status": "active",
"balance": {
"kobo": 0,
"naira": "0.00"
},
"nuban": "0123456789",
"bank_name": "PayKore MFB",
"metadata": {"plan": "premium"},
"created_at": "2025-06-01T10:00:00Z",
"updated_at": "2025-06-01T10:00:00Z",
"meta": {
"request_id": "req_7hJkL9mNpQ"
}
}
TypeScript SDK:
const wallet = await paykore.wallets.create({
userRef: 'user_123',
currency: 'NGN',
metadata: { plan: 'premium' },
});
console.log(wallet.id); // "wlt_9f3kA2mXpQ"
console.log(wallet.nuban); // "0123456789" — the user's bank account number
The nuban and bank_name fields give you the user's real Nigerian bank account number. Display these to your user so they can receive external transfers directly into their wallet.
Fetching a wallet and balance
curl https://api.paykore.dev/v1/wallets/wlt_9f3kA2mXpQ \
-H "X-API-Key: sk_test_YOUR_KEY_HERE"
The balance in the response is computed from the ledger in real time and cached for 60 seconds. For the exact balance (bypassing cache), add ?nocache=true:
curl "https://api.paykore.dev/v1/wallets/wlt_9f3kA2mXpQ?nocache=true" \
-H "X-API-Key: sk_test_YOUR_KEY_HERE"
Use ?nocache=true sparingly — for example, immediately after a credit to confirm the balance updated. For display purposes (dashboards, transaction lists), the 60-second cache is fine and reduces load.
Wallet status
| Status | Can debit | Can credit | Description |
|---|---|---|---|
pending | No | No | Wallet just created; NUBAN being provisioned by MFB (usually under 5 seconds). |
active | Yes | Yes | Normal operating state. |
frozen | No | Yes | Outbound blocked. Can still receive funds. Used for compliance holds. |
closed | No | No | Permanent. No further operations possible. Create a new wallet if needed. |
Check status from the response status field. If a debit fails with WALLET_FROZEN or WALLET_CLOSED, check the wallet's current status before retrying.
Transaction history
curl "https://api.paykore.dev/v1/wallets/wlt_9f3kA2mXpQ/transactions?type=p2p_transfer&from=2025-06-01T00:00:00Z&limit=20" \
-H "X-API-Key: sk_test_YOUR_KEY_HERE"
Query parameters:
| Param | Description |
|---|---|
type | Filter by type: p2p_transfer, bank_transfer, ussd_payment, qr_payment, funding |
status | Filter by status: pending, processing, completed, failed |
from | ISO 8601 start date (inclusive) |
to | ISO 8601 end date (inclusive) |
limit | Results per page (default 20, max 100) |
cursor | Cursor for next page (from previous response pagination.next_cursor) |
Response:
{
"data": [
{
"id": "tx_4qYzA1bCdE",
"type": "p2p_transfer",
"status": "completed",
"amount_kobo": 500000,
"amount_naira": "5000.00",
"direction": "debit",
"created_at": "2025-06-01T10:03:00Z"
}
],
"pagination": {
"total": 42,
"limit": 20,
"has_more": true,
"next_cursor": "cur_aB1cD2eF3g"
},
"meta": {
"request_id": "req_5mNoPqRsTu"
}
}
To fetch the next page, pass the cursor:
curl "https://api.paykore.dev/v1/wallets/wlt_9f3kA2mXpQ/transactions?cursor=cur_aB1cD2eF3g" \
-H "X-API-Key: sk_test_YOUR_KEY_HERE"
Common patterns
Create a wallet on user sign-up
The standard pattern is to create a PayKore wallet when a user registers in your system, then store the wallet ID in your database:
// In your user registration handler
async function registerUser(email: string, password: string) {
// 1. Create user in your database
const user = await db.users.create({ email, password });
// 2. Create PayKore wallet
const wallet = await paykore.wallets.create({
userRef: user.id,
currency: 'NGN',
});
// 3. Store wallet ID on the user record
await db.users.update(user.id, { paykoreWalletId: wallet.id });
return user;
}
This gives every user a wallet immediately. You look up paykoreWalletId from your own database whenever you need to debit or credit that user — no need to call GET /v1/wallets just to get the ID.
Handle duplicate userRef
If you call POST /v1/wallets for a userRef that already has a wallet in that currency, PayKore returns 409 DUPLICATE_WALLET:
{
"error": {
"code": "DUPLICATE_WALLET",
"message": "A wallet for userRef 'user_123' in NGN already exists.",
"http_status": 409,
"existing_wallet_id": "wlt_9f3kA2mXpQ"
}
}
The existing_wallet_id field is included so you can fetch or store it without a separate lookup. Handle this gracefully rather than treating it as a fatal error — it commonly occurs if a user signs up twice or if your registration handler retries:
try {
const wallet = await paykore.wallets.create({ userRef: user.id, currency: 'NGN' });
return wallet;
} catch (err) {
if (err.code === 'DUPLICATE_WALLET') {
// Already exists — fetch it
return await paykore.wallets.get(err.existingWalletId);
}
throw err;
}
SDK examples
TypeScript:
import { PayKore } from '@paykore/sdk';
const paykore = new PayKore({ apiKey: process.env.PAYKORE_API_KEY });
// Create
const wallet = await paykore.wallets.create({ userRef: 'user_123', currency: 'NGN' });
// Fetch
const fetched = await paykore.wallets.get('wlt_9f3kA2mXpQ');
// Transactions
const txns = await paykore.wallets.transactions('wlt_9f3kA2mXpQ', {
type: 'p2p_transfer',
limit: 20,
});
React Native:
import { PayKoreClient } from '@paykore/react-native-sdk';
const client = new PayKoreClient({ apiKey: PAYKORE_API_KEY });
const wallet = await client.wallets.create({ userRef: currentUser.id, currency: 'NGN' });
Next steps
- P2P Transfers → — Move money between wallets instantly.
- Bank Transfers → — Send funds to external Nigerian bank accounts.
- Core Concepts → — Understand wallet status, ledger entries, and balances.