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
| Header | Value | Required | Description |
|---|---|---|---|
Accept | application/json | Yes | Content type for the response |
Authorization | Bearer {access_token} | Yes | Bearer token for authentication |
x-api-key | API Key | Yes | API key for authentication |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page_no | integer | No | Page number, start from 1 (default: 1) |
page_size | integer | No | Number of results per page (default: 20) |
card_status | string | No | Current card status. One of: INACTIVE, ACTIVE, REVOKED, CANCELED |
last4 | string | No | Last 4 digits of the card number |
remark | string | No | An optional description to help identify the card |
start_date | string | No | Card valid from date (format: yyyy-MM-dd HH:mm:ss) |
end_date | string | No | Card 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
| Field | Type | Description |
|---|---|---|
code | string | Status string indicating the result. "SUCCESS" refers to a successful query |
data | object | Response data object |
data.total_num | integer | Total number of cards meeting the query conditions |
data.list | array | List of card details |
data.list[].card_status | string | Card status: INACTIVE, ACTIVE, REVOKED, CANCELED |
data.list[].cancellation_in_progress | string | "true": Card cancellation applied; "false": Not applied for cancellation |
data.list[].card_cancelled | string | "true": Card has been cancelled; "false": Card is active |
data.list[].card_number | string | A unique 16-digit card number |
data.list[].budget_id | string | ID of the budget account this card sources from (Shared Funds Cards) |
data.list[].created | string | Card creation date and time (format: yyyy-MM-dd HH:mm:ss) |
data.list[].billing_currency | string | The currency for card transactions (e.g., USD) |
data.list[].remark | string | Optional description to identify the card |
data.list[].type | string | NORMAL for Dedicated Funds Card; SHARE for Shared Funds Card |
data.list[].card_id | string | A unique ID of the card |
data.list[].allow_withdrawal | string | "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:ssfor date parameters - Card Types:
SHARE(Shared Funds) orNORMAL(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
Related Endpoints
- 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