Skip to main content

Query All Cards

Overview

Use this endpoint to query card details of all cards in your account with powerful filtering and pagination capabilities. This is essential for card management dashboards, reporting, and administrative tasks.

NOTE: This API is offered on a client-by-client approval basis. Speak to your account manager to check eligibility.

Resource Access

Production (api.ahrvo.network)

GET https://api.ahrvo.network/card/issuance/api/issuing/card/v2/detail-list

Staging (gateway.ahrvo.network)

GET https://gateway.ahrvo.network/card/issuance/api/issuing/card/v2/detail-list

Request Headers

HeaderValueRequiredDescription
Acceptapplication/jsonYesContent type for the response
AuthorizationBearer {access_token}YesBearer token for authentication
x-api-keyAPI KeyYesAPI key for authentication

Query Parameters

ParameterTypeRequiredDescription
page_nointegerNoPage number, start from 1 (default: 1)
page_sizeintegerNoNumber of results per page (default: 20)
card_statusstringNoCurrent card status. One of: INACTIVE, ACTIVE, REVOKED, CANCELED
last4stringNoLast 4 digits of the card number
remarkstringNoAn optional description to help identify the card
start_datestringNoCard valid from date (format: yyyy-MM-dd HH:mm:ss)
end_datestringNoCard valid to date (format: yyyy-MM-dd HH:mm:ss)

Response

Success Response (200 OK)

{
"code": "SUCCESS",
"data": {
"total_num": 1,
"list": [
{
"card_status": "ACTIVE",
"cancellation_in_progress": "false",
"card_cancelled": "false",
"card_number": "5329111111112321",
"budget_id": "ci202008281528472924",
"created": "2023-08-17 16:39:22",
"billing_currency": "USD",
"remark": "This is my test card",
"type": "SHARE",
"card_id": "card20820230817163921206780",
"allow_withdrawal": "false"
}
]
}
}

Response Fields

FieldTypeDescription
codestringStatus string indicating the result. "SUCCESS" refers to a successful query
dataobjectResponse data object
data.total_numintegerTotal number of cards meeting the query conditions
data.listarrayList of card details
data.list[].card_statusstringCard status: INACTIVE, ACTIVE, REVOKED, CANCELED
data.list[].cancellation_in_progressstring"true": Card cancellation applied; "false": Not applied for cancellation
data.list[].card_cancelledstring"true": Card has been cancelled; "false": Card is active
data.list[].card_numberstringA unique 16-digit card number
data.list[].budget_idstringID of the budget account this card sources from (Shared Funds Cards)
data.list[].createdstringCard creation date and time (format: yyyy-MM-dd HH:mm:ss)
data.list[].billing_currencystringThe currency for card transactions (e.g., USD)
data.list[].remarkstringOptional description to identify the card
data.list[].typestringNORMAL for Dedicated Funds Card; SHARE for Shared Funds Card
data.list[].card_idstringA unique ID of the card
data.list[].allow_withdrawalstring"true": Withdrawals allowed; "false": Withdrawals not allowed

Error Responses

  • 400 Bad Request: Invalid query parameters
  • 401 Unauthorized: Invalid or missing authentication token
  • 403 Forbidden: Card issuance feature not enabled for this account

Code Examples

cURL

curl -X GET \
'https://gateway.ahrvo.network/card/issuance/api/issuing/card/v2/detail-list?page_no=1&page_size=10&card_status=ACTIVE' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'x-api-key: YOUR_API_KEY'

Python

import requests

url = "https://gateway.ahrvo.network/card/issuance/api/issuing/card/v2/detail-list"
headers = {
"Accept": "application/json",
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"x-api-key": "YOUR_API_KEY"
}
params = {
"page_no": 1,
"page_size": 10,
"card_status": "ACTIVE"
}

response = requests.get(url, headers=headers, params=params)
result = response.json()

if result['code'] == 'SUCCESS':
total = result['data']['total_num']
cards = result['data']['list']

print(f"Total cards: {total}")
print(f"Retrieved: {len(cards)} cards\n")

for card in cards:
print(f"Card ID: {card['card_id']}")
print(f" Number: {card['card_number']}")
print(f" Status: {card['card_status']}")
print(f" Type: {card['type']}")
print(f" Created: {card['created']}")
print(f" Remark: {card.get('remark', 'N/A')}\n")
else:
print(f"Failed to query cards: {result}")

JavaScript (Node.js)

const axios = require('axios');

const url = 'https://gateway.ahrvo.network/card/issuance/api/issuing/card/v2/detail-list';
const headers = {
'Accept': 'application/json',
'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
'x-api-key': 'YOUR_API_KEY'
};
const params = {
page_no: 1,
page_size: 10,
card_status: 'ACTIVE'
};

axios.get(url, { headers, params })
.then(response => {
const result = response.data;

if (result.code === 'SUCCESS') {
const total = result.data.total_num;
const cards = result.data.list;

console.log(`Total cards: ${total}`);
console.log(`Retrieved: ${cards.length} cards\n`);

cards.forEach(card => {
console.log(`Card ID: ${card.card_id}`);
console.log(` Number: ${card.card_number}`);
console.log(` Status: ${card.card_status}`);
console.log(` Type: ${card.type}`);
console.log(` Created: ${card.created}`);
console.log(` Remark: ${card.remark || 'N/A'}\n`);
});
}
})
.catch(error => {
console.error('Failed to query cards:', error.response.data);
});

Usage Notes

  • Pagination: Results are paginated with default page size of 20
  • Filters: Combine multiple filters for precise queries
  • Date Format: Use yyyy-MM-dd HH:mm:ss for date parameters
  • Card Types: SHARE (Shared Funds) or NORMAL (Dedicated Funds)
  • Status Values: INACTIVE, ACTIVE, REVOKED, CANCELED

Common Use Cases

Query Active Cards Only

Retrieve all active cards in your account:

def get_active_cards():
"""
Get all active cards with pagination
"""
all_cards = []
page_no = 1
page_size = 50

print("Fetching all active cards...\n")

while True:
params = {
"page_no": page_no,
"page_size": page_size,
"card_status": "ACTIVE"
}

response = query_all_cards(params)

if response['code'] == 'SUCCESS':
data = response['data']
cards = data['list']
total = data['total_num']

all_cards.extend(cards)

print(f"Page {page_no}: Retrieved {len(cards)} cards")
print(f"Total progress: {len(all_cards)}/{total}")

# Check if we've retrieved all cards
if len(all_cards) >= total:
break

page_no += 1
else:
print(f"Error fetching cards: {response}")
break

print(f"\nTotal active cards retrieved: {len(all_cards)}\n")

# Categorize by type
shared_cards = [c for c in all_cards if c['type'] == 'SHARE']
dedicated_cards = [c for c in all_cards if c['type'] == 'NORMAL']

print(f"Breakdown:")
print(f" Shared Funds Cards: {len(shared_cards)}")
print(f" Dedicated Funds Cards: {len(dedicated_cards)}")

return {
'total': len(all_cards),
'shared': shared_cards,
'dedicated': dedicated_cards,
'all': all_cards
}

# Example usage
active_cards = get_active_cards()

Search Cards by Last 4 Digits

Find a specific card by its last 4 digits:

async function findCardByLast4(last4Digits) {
console.log(`\nSearching for card ending in ${last4Digits}...\n`);

try {
const response = await queryAllCards({
last4: last4Digits,
page_size: 100
});

if (response.code === 'SUCCESS') {
const cards = response.data.list;

if (cards.length === 0) {
console.log(`No cards found ending in ${last4Digits}`);
return null;
}

console.log(`Found ${cards.length} card(s):\n`);

cards.forEach((card, index) => {
console.log(`Card ${index + 1}:`);
console.log(` Card ID: ${card.card_id}`);
console.log(` Card Number: ${card.card_number}`);
console.log(` Status: ${card.card_status}`);
console.log(` Type: ${card.type === 'SHARE' ? 'Shared Funds' : 'Dedicated Funds'}`);
console.log(` Created: ${card.created}`);
console.log(` Budget ID: ${card.budget_id || 'N/A'}`);
console.log(` Remark: ${card.remark || 'None'}\n`);
});

return cards;
}
} catch (error) {
console.error(`Error searching for card: ${error.message}`);
return null;
}
}

// Example usage
const cards = await findCardByLast4('7506');

Generate Card Inventory Report

Create a comprehensive inventory report of all cards:

def generate_card_inventory_report():
"""
Generate comprehensive inventory report for all cards
"""
from datetime import datetime
from collections import defaultdict

print("=== Card Inventory Report ===")
print(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")

# Fetch all cards (no status filter)
all_cards = []
page_no = 1

while True:
response = query_all_cards({
"page_no": page_no,
"page_size": 100
})

if response['code'] == 'SUCCESS':
cards = response['data']['list']
total = response['data']['total_num']

all_cards.extend(cards)

if len(all_cards) >= total:
break

page_no += 1
else:
break

# Statistics
status_breakdown = defaultdict(int)
type_breakdown = defaultdict(int)
budget_cards = defaultdict(list)

for card in all_cards:
status_breakdown[card['card_status']] += 1
type_breakdown[card['type']] += 1

if card.get('budget_id'):
budget_cards[card['budget_id']].append(card)

# Print summary
print(f"📊 Overall Summary")
print(f" Total Cards: {len(all_cards)}\n")

print(f"📈 Status Breakdown:")
for status, count in sorted(status_breakdown.items()):
percentage = (count / len(all_cards)) * 100
bar = '█' * int(percentage / 2)
print(f" {status.ljust(10)}: {count:3d} ({percentage:5.1f}%) {bar}")
print()

print(f"💳 Card Type Breakdown:")
for card_type, count in type_breakdown.items():
type_name = "Shared Funds" if card_type == "SHARE" else "Dedicated Funds"
percentage = (count / len(all_cards)) * 100
print(f" {type_name}: {count} ({percentage:.1f}%)")
print()

print(f"💰 Budget Distribution:")
print(f" Unique Budgets: {len(budget_cards)}")
if budget_cards:
print(f" Cards per Budget:")
for budget_id, cards in sorted(budget_cards.items(), key=lambda x: len(x[1]), reverse=True)[:5]:
print(f" {budget_id}: {len(cards)} cards")
print()

# Cards with remarks
cards_with_remarks = [c for c in all_cards if c.get('remark')]
print(f"📝 Cards with Remarks: {len(cards_with_remarks)} ({len(cards_with_remarks)/len(all_cards)*100:.1f}%)")

# Cancellation status
pending_cancellation = [c for c in all_cards if c.get('cancellation_in_progress') == 'true']
if pending_cancellation:
print(f"⚠️ Cards Pending Cancellation: {len(pending_cancellation)}")

# Recent cards (created in last 30 days)
from datetime import timedelta
thirty_days_ago = datetime.now() - timedelta(days=30)
recent_cards = []
for card in all_cards:
try:
created_date = datetime.strptime(card['created'], '%Y-%m-%d %H:%M:%S')
if created_date >= thirty_days_ago:
recent_cards.append(card)
except:
pass

print(f"\n🆕 Recent Cards (Last 30 days): {len(recent_cards)}")

return {
'total_cards': len(all_cards),
'status_breakdown': dict(status_breakdown),
'type_breakdown': dict(type_breakdown),
'budget_distribution': {k: len(v) for k, v in budget_cards.items()},
'cards_with_remarks': len(cards_with_remarks),
'pending_cancellation': len(pending_cancellation),
'recent_cards': len(recent_cards),
'all_cards': all_cards
}

# Example usage
report = generate_card_inventory_report()

Filter Cards by Date Range

Query cards created within a specific date range:

async function getCardsByDateRange(startDate, endDate) {
console.log(`\n=== Cards Created Between ${startDate} and ${endDate} ===\n`);

const params = {
start_date: startDate,
end_date: endDate,
page_size: 100
};

let allCards = [];
let pageNo = 1;

while (true) {
params.page_no = pageNo;

try {
const response = await queryAllCards(params);

if (response.code === 'SUCCESS') {
const cards = response.data.list;
const total = response.data.total_num;

allCards = allCards.concat(cards);

console.log(`Page ${pageNo}: Retrieved ${cards.length} cards`);

if (allCards.length >= total) {
break;
}

pageNo++;
} else {
console.error('Error fetching cards:', response);
break;
}
} catch (error) {
console.error('Request failed:', error.message);
break;
}
}

console.log(`\nTotal cards found: ${allCards.length}\n`);

// Group by month
const cardsByMonth = {};

allCards.forEach(card => {
const month = card.created.substring(0, 7); // Extract YYYY-MM
if (!cardsByMonth[month]) {
cardsByMonth[month] = [];
}
cardsByMonth[month].push(card);
});

console.log('Breakdown by Month:');
Object.keys(cardsByMonth).sort().forEach(month => {
const count = cardsByMonth[month].length;
const bar = '█'.repeat(Math.ceil(count / 5));
console.log(` ${month}: ${count.toString().padStart(3)} cards ${bar}`);
});

return {
total: allCards.length,
by_month: cardsByMonth,
cards: allCards
};
}

// Example usage
const cards = await getCardsByDateRange(
'2023-01-01 00:00:00',
'2023-12-31 23:59:59'
);

Export Card List to CSV

Export card data to CSV format for analysis:

def export_cards_to_csv(filename='cards_export.csv', filters=None):
"""
Export all cards matching filters to CSV file

Args:
filename: Output CSV filename
filters: Dictionary of filter parameters (optional)
"""
import csv

print(f"Exporting cards to {filename}...\n")

# Fetch all cards
all_cards = []
page_no = 1

params = filters or {}
params['page_size'] = 100

while True:
params['page_no'] = page_no

response = query_all_cards(params)

if response['code'] == 'SUCCESS':
cards = response['data']['list']
total = response['data']['total_num']

all_cards.extend(cards)

print(f"Fetched page {page_no}: {len(all_cards)}/{total} cards")

if len(all_cards) >= total:
break

page_no += 1
else:
print(f"Error: {response}")
return None

# Write to CSV
if all_cards:
fieldnames = [
'card_id',
'card_number',
'card_status',
'type',
'budget_id',
'created',
'billing_currency',
'remark',
'allow_withdrawal',
'cancellation_in_progress',
'card_cancelled'
]

with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()

for card in all_cards:
# Map card type to readable name
card_copy = card.copy()
card_copy['type'] = 'Shared Funds' if card['type'] == 'SHARE' else 'Dedicated Funds'

# Only write fields that exist
row = {field: card_copy.get(field, '') for field in fieldnames}
writer.writerow(row)

print(f"\n✓ Successfully exported {len(all_cards)} cards to {filename}")
print(f" File size: {os.path.getsize(filename)} bytes")

return {
'filename': filename,
'count': len(all_cards),
'cards': all_cards
}
else:
print("No cards to export")
return None

# Example usage - Export all active cards
result = export_cards_to_csv(
filename='active_cards_2024.csv',
filters={'card_status': 'ACTIVE'}
)

# Export cards by budget
result = export_cards_to_csv(
filename='budget_cards.csv',
filters={'card_status': 'ACTIVE'}
)

Monitor Cards by Remark/Tag

Query and monitor cards with specific remarks or tags:

async function monitorCardsByRemark(remarkKeywords) {
console.log('\n=== Card Monitoring by Remark ===\n');
console.log(`Keywords: ${remarkKeywords.join(', ')}\n`);

const matchingCards = {};

// Initialize result structure
remarkKeywords.forEach(keyword => {
matchingCards[keyword] = [];
});

// Fetch all cards
let pageNo = 1;
let hasMore = true;

while (hasMore) {
try {
const response = await queryAllCards({
page_no: pageNo,
page_size: 100
});

if (response.code === 'SUCCESS') {
const cards = response.data.list;
const total = response.data.total_num;

// Check each card's remark against keywords
cards.forEach(card => {
if (card.remark) {
remarkKeywords.forEach(keyword => {
if (card.remark.toLowerCase().includes(keyword.toLowerCase())) {
matchingCards[keyword].push(card);
}
});
}
});

// Check if we need to fetch more pages
hasMore = (pageNo * 100) < total;
pageNo++;
} else {
hasMore = false;
}
} catch (error) {
console.error('Error fetching cards:', error.message);
hasMore = false;
}
}

// Display results
console.log('Results:\n');

remarkKeywords.forEach(keyword => {
const cards = matchingCards[keyword];
console.log(`📌 Keyword: "${keyword}"`);
console.log(` Matches: ${cards.length} cards\n`);

if (cards.length > 0) {
cards.forEach(card => {
console.log(`${card.card_number} (${card.card_status})`);
console.log(` Remark: ${card.remark}`);
console.log(` Created: ${card.created}\n`);
});
}
});

// Summary
const totalMatches = Object.values(matchingCards).flat().length;
console.log(`\n=== Summary ===`);
console.log(`Total Matching Cards: ${totalMatches}`);

return matchingCards;
}

// Example usage
const results = await monitorCardsByRemark([
'marketing',
'sales',
'travel',
'expense'
]);

Audit Cards Pending Cancellation

Identify and track cards that are pending cancellation:

def audit_pending_cancellations():
"""
Audit all cards with pending cancellation status
"""
from datetime import datetime

print("\n=== Cards Pending Cancellation Audit ===\n")

# Fetch all cards
all_cards = []
page_no = 1

while True:
response = query_all_cards({
"page_no": page_no,
"page_size": 100
})

if response['code'] == 'SUCCESS':
cards = response['data']['list']
total = response['data']['total_num']
all_cards.extend(cards)

if len(all_cards) >= total:
break
page_no += 1
else:
break

# Filter pending cancellations
pending = [
card for card in all_cards
if card.get('cancellation_in_progress') == 'true'
]

if not pending:
print("✓ No cards pending cancellation\n")
return {
'count': 0,
'cards': []
}

print(f"⚠️ Found {len(pending)} card(s) pending cancellation:\n")

for i, card in enumerate(pending, 1):
print(f"{i}. Card ID: {card['card_id']}")
print(f" Card Number: {card['card_number']}")
print(f" Status: {card['card_status']}")
print(f" Type: {'Shared Funds' if card['type'] == 'SHARE' else 'Dedicated Funds'}")
print(f" Created: {card['created']}")
print(f" Remark: {card.get('remark', 'N/A')}")
print(f" Budget ID: {card.get('budget_id', 'N/A')}")

# Calculate days since creation
try:
created = datetime.strptime(card['created'], '%Y-%m-%d %H:%M:%S')
days_old = (datetime.now() - created).days
print(f" Age: {days_old} days")
except:
pass

print()

print(f"=== Summary ===")
print(f"Pending Cancellations: {len(pending)}")
print(f"Recommendation: Follow up on cancellation progress\n")

return {
'count': len(pending),
'cards': pending
}

# Example usage
audit_results = audit_pending_cancellations()

Best Practices

  • Pagination: Always use pagination for large card portfolios to avoid timeouts
  • Efficient Filtering: Apply filters at the API level rather than fetching all cards and filtering locally
  • Regular Exports: Schedule regular card inventory exports for backup and analysis
  • Status Monitoring: Monitor card status changes to detect unusual patterns
  • Remark Usage: Use consistent remark/tagging conventions for easier filtering
  • Rate Limiting: Implement reasonable delays between bulk queries

Understanding Card Statuses

INACTIVE

  • Card has been created but not yet activated
  • Cannot be used for transactions
  • Typically new cards awaiting activation

ACTIVE

  • Card is fully operational
  • Can process transactions
  • Normal working state

REVOKED

  • Card has been temporarily suspended
  • Cannot process transactions
  • Can potentially be reactivated

CANCELED

  • Card has been permanently closed
  • Cannot be reactivated
  • Terminal state
  • Create a Card - Issue new cards
  • Query Card Details - Get detailed information for a specific card
  • Control Card Spending - Manage spending limits
  • Query Card Remaining Limits - Check available spending capacity
  • Update Card - Modify card properties
  • Cancel Card - Close a card permanently

Troubleshooting

No Results Returned

  • Cause: Filters are too restrictive or no cards match criteria
  • Solution:
    • Remove filters one by one to identify the issue
    • Verify filter values are correct
    • Check if cards exist in your account

Pagination Issues

  • Cause: Incorrect page_no or page_size parameters
  • Solution:
    • Ensure page_no starts from 1
    • Use reasonable page_size (10-100)
    • Check total_num to determine available pages

Missing Card Data

  • Cause: Some fields may be null/empty depending on card configuration
  • Solution:
    • Handle null values gracefully in your code
    • Use .get() method with defaults in Python
    • Use optional chaining (?.) in JavaScript

Security Considerations

  • Data Privacy: Card numbers are sensitive - restrict access appropriately
  • Audit Logging: Log all card queries for security auditing
  • Access Control: Implement role-based access to card lists
  • Export Security: Encrypt exported CSV files containing card data
  • API Rate Limits: Respect rate limits to avoid service disruption

Interactive API Explorer