Multi-Card Cardholder Management
A single approved cardholder can hold multiple cards simultaneously — each with its own balance, transaction limit, deposit addresses, and status. This recipe covers the patterns for listing, managing, and presenting a consolidated view of a multi-card cardholder.
Listing all cards for a cardholder
const { api } = require('./client');
async function getCardsForCardholder(cardholderId, statusFilter = null) {
const params = new URLSearchParams({ cardholderId });
if (statusFilter !== null) {
params.set('issuerCardStatus', statusFilter);
}
const result = await api('GET', `/cards?${params}`);
return result.items ?? [];
}
// Example: get only active cards
const activeCards = await getCardsForCardholder(10532, 1);Consolidated balance view across all cards
async function consolidatedBalance(cardholderId) {
const cards = await getCardsForCardholder(cardholderId);
const balances = await Promise.all(
cards.map(async card => {
const bal = await api('GET', `/cards/${card.cardId}/balance`);
return {
cardId: card.cardId,
alias: card.alias ?? `Card ${card.cardId}`,
type: card.type,
maskedPan: card.maskedCardNumber,
issuerCardStatus: card.status, // from list response = issuerCardStatus
ledgerBalance: bal.ledgerBalance,
availableBalance: bal.availableBalance,
currency: bal.currency,
};
})
);
const totals = balances.reduce((acc, b) => ({
totalLedger: acc.totalLedger + (b.ledgerBalance ?? 0),
totalAvailable: acc.totalAvailable + (b.availableBalance ?? 0),
}), { totalLedger: 0, totalAvailable: 0 });
return { cards: balances, totals };
}Example output:
{
"cards": [
{ "cardId": 8821, "alias": "Personal Spending", "type": "virtual",
"issuerCardStatus": 1, "ledgerBalance": 50000, "availableBalance": 45000, "currency": 0 },
{ "cardId": 9104, "alias": "Travel Card", "type": "physical",
"issuerCardStatus": 1, "ledgerBalance": 150000, "availableBalance": 150000, "currency": 0 }
],
"totals": { "totalLedger": 200000, "totalAvailable": 195000 }
}Disambiguating deposits across multiple cards
When a cardholder holds multiple cards, it's important to route deposits to the correct card. Each card has its own independent cryptoAddresses — there's no ambiguity on the crypto rail. On the bank rail, the payment reference must identify the specific cardId:
async function getFundingDetails(cardId) {
const card = await api('GET', `/cards/${cardId}`);
return {
cardId: card.cardId,
alias: card.alias,
cryptoAddresses: card.cryptoAddresses,
// Bank deposit reference: the cardId is used as the reference
// (confirm reference format with your Axys account team)
bankReference: `CARD-${card.cardId}`,
// Reminder: bank inbound credentials come from Account Management
bankNote: 'Inbound bank account details available via Account Management only.',
};
}Per-card limit management
Transaction limits cascade Program → Cardholder → Card. To adjust a specific card's limit without affecting others:
async function setCardLimit(cardId, newLimitDecimalImplied) {
// Validate: cannot exceed cardholder's limit
// (you'd need to GET /cardholders/{id} and check their transactionLimit first)
const result = await api('PUT', `/cards/${cardId}/update`, {
transactionLimit: newLimitDecimalImplied,
});
console.log(`Card ${cardId} limit updated to ${newLimitDecimalImplied}`);
return result;
}
// Example: set card 8821 to $300.00 max
await setCardLimit(8821, 30000);Bulk block / unblock
To temporarily suspend all cards for a cardholder (e.g. suspected fraud while investigating):
async function bulkSetCardStatus(cardholderId, newStatus) {
// Only toggle between Active (1) and On Hold (2)
if (![1, 2].includes(newStatus)) {
throw new Error('newStatus must be 1 (Active) or 2 (On Hold)');
}
const cards = await getCardsForCardholder(cardholderId);
// Only update cards currently in a toggleable status
const eligible = cards.filter(c => c.status === 1 || c.status === 2);
const results = await Promise.allSettled(
eligible.map(card =>
api('PUT', `/cards/${card.cardId}/status`, { newStatus })
.then(() => ({ cardId: card.cardId, success: true }))
.catch(e => ({ cardId: card.cardId, success: false, error: e.message }))
)
);
const succeeded = results.filter(r => r.value?.success).length;
const failed = results.filter(r => !r.value?.success).length;
console.log(`Bulk status update: ${succeeded} succeeded, ${failed} failed`);
return results.map(r => r.value);
}
// Block all active/on-hold cards for cardholder 10532
await bulkSetCardStatus(10532, 2);To permanently close a card or report it lost/stolen, raise a support ticket —
issuerCardStatus = 3(Closed) is not reachable viaPUT /cards/{cardId}/status. See Support Tickets.
Funding multiple cards from a wallet
The corporate pattern: distribute from a central Wallet to multiple Cards in one pass:
async function distributeFromWallet(walletId, distributions) {
// distributions: [{ cardId, amount, description }, ...]
const results = [];
for (const { cardId, amount, description } of distributions) {
try {
const result = await api('POST', '/wallets/transfer', {
sourceWalletId: walletId,
destinationType: 1, // 1 = card
destinationId: cardId,
amount,
description: description ?? `Allocation to card ${cardId}`,
});
results.push({ cardId, success: true, transId: result.transId,
transStatus: result.transStatus });
// Brief pause between transfers to avoid rate limiting
await sleep(200);
} catch (err) {
results.push({ cardId, success: false, error: err.message });
}
}
return results;
}
// Example: fund three cards from wallet 3301
await distributeFromWallet(3301, [
{ cardId: 8821, amount: 25000, description: 'Q3 allocation — Jordan Reyes' },
{ cardId: 9104, amount: 50000, description: 'Q3 allocation — Alex Chen' },
{ cardId: 9201, amount: 25000, description: 'Q3 allocation — Sam Okafor' },
]);No idempotency key available. If a transfer call times out, verify via
GET /cards/{cardId}/balancebefore retrying — do not blindly retry a transfer that may have succeeded. See Transfer Internal Fiat Funds.
