Overview
The Transaction API provides comprehensive access to card transaction history with powerful filtering, pagination, and statement generation capabilities. View detailed transaction information including merchant details, amounts, fees, and crypto funding sources.
Transactions are returned in reverse chronological order (most recent first) for easy access to recent activity.
Transaction Data Structure
Each transaction includes detailed information across multiple dimensions:
Transaction Details ID, timestamp, merchant, type, MCC category
Financial Data Amounts, fees, currencies, conversion rates
Funding Sources Crypto wallets, networks, transaction hashes
Sample Transaction
{
"id" : "100a99cf-f4d3-4fa1-9be9-2e9828b20ebb" ,
"cardId" : "1234537292209260487" ,
"panLast4" : "9189" ,
"transactionId" : "1122334477422" ,
"dateTime" : "2024-10-14T10:44:36.276Z" ,
"sign" : "DEBIT" ,
"merchantNameLocation" : "WWW.ALIEXPRESS.COM, LONDON" ,
"merchantType" : "OutOfWalletOnline" ,
"mcc" : 5964 ,
"mccCategory" : "MISC" ,
"transactionCurrency" : "EUR" ,
"amountInTransactionCurrency" : "0.79" ,
"feesInTransactionCurrency" : "0" ,
"originalCurrency" : "USD" ,
"amountInOriginalCurrency" : "0.85" ,
"feesInOriginalCurrency" : "0" ,
"billingConversionRate" : "0.9294117647058824" ,
"ecbRate" : "0.9161704076958315" ,
"status" : "CONFIRMED" ,
"declineReason" : "" ,
"fundingSources" : [
{
"id" : "3181a37a-07fa-41dc-b423-6c2db07a7ba1" ,
"address" : "0x3a11a86cf218c448be519728cd3ac5c741fb3424" ,
"network" : "linea" ,
"txHash" : "0xb92de09d893e8162b0861c0f7321f68df02212efbc58f208839ae3f176d89638" ,
"currency" : "usdc" ,
"amount" : "0.104201" ,
"fees" : "0" ,
"swapFee" : "0.00208" ,
"sign" : "DEBIT" ,
"status" : "CONFIRMED" ,
"dateTime" : "2024-10-14T10:44:36.288Z"
}
]
}
Fetching Transaction History
Basic Request
JavaScript/Node.js
Python
cURL
const response = await fetch ( 'https://dev.api.baanx.com/v1/card/transactions' , {
headers: {
'X-Client-ID' : 'your_client_id' ,
'Authorization' : `Bearer ${ userAccessToken } `
}
});
const transactions = await response . json ();
console . log ( `Retrieved ${ transactions . length } transactions` );
// Fetch page 2 (0-indexed, so page=1 is actually the second page)
const response = await fetch (
'https://dev.api.baanx.com/v1/card/transactions?page=1' ,
{
headers: {
'X-Client-ID' : clientId ,
'Authorization' : `Bearer ${ accessToken } `
}
}
);
const transactions = await response . json ();
Pages are 0-indexed. page=0 is the first page, page=1 is the second page, etc. If you request a page that doesn’t exist, it returns page 0 (first page).
Filtering Transactions
By Date Range
Filter transactions within a specific time period:
JavaScript/Node.js
Python
cURL
const params = new URLSearchParams ({
dateFrom: '2024-10-01' ,
dateTo: '2024-10-31'
});
const response = await fetch (
`https://dev.api.baanx.com/v1/card/transactions? ${ params } ` ,
{
headers: {
'X-Client-ID' : clientId ,
'Authorization' : `Bearer ${ accessToken } `
}
}
);
const transactions = await response . json ();
Start date in ISO 8601 format (YYYY-MM-DD). Required if dateTo is specified. Example: 2024-10-01
End date in ISO 8601 format (YYYY-MM-DD). Required if dateFrom is specified. Example: 2024-10-31
Both dateFrom and dateTo must be provided together. Specifying only one will result in an error.
By Merchant Name
Search for transactions from specific merchants:
const params = new URLSearchParams ({
searchKey: 'PayPal'
});
const response = await fetch (
`https://dev.api.baanx.com/v1/card/transactions? ${ params } ` ,
{
headers: {
'X-Client-ID' : clientId ,
'Authorization' : `Bearer ${ accessToken } `
}
}
);
Search term to filter merchant names. Case-insensitive partial match. Example: "PayPal", "Amazon", "Starbucks"
By Category
Filter by merchant category code (MCC) category:
const params = new URLSearchParams ({
mccCategories: 'FOOD,TRAVEL'
});
const response = await fetch (
`https://dev.api.baanx.com/v1/card/transactions? ${ params } ` ,
{
headers: {
'X-Client-ID' : clientId ,
'Authorization' : `Bearer ${ accessToken } `
}
}
);
Comma-separated list of categories to filter by. Available Categories:
SUBSCRIPTIONS: Streaming services, memberships
FOOD: Restaurants, groceries, delivery
TRAVEL: Hotels, airlines, car rentals
ENTERTAINMENT: Movies, concerts, games
HEALTH: Healthcare, pharmacy, fitness
ATM: ATM withdrawals
UTILITIES: Bills, phone, internet
MISC: Everything else
Example: "FOOD,ENTERTAINMENT", "TRAVEL"
Combined Filters
All filters can be combined:
const params = new URLSearchParams ({
dateFrom: '2024-10-01' ,
dateTo: '2024-10-31' ,
searchKey: 'uber' ,
mccCategories: 'TRAVEL,FOOD' ,
page: 0
});
const response = await fetch (
`https://dev.api.baanx.com/v1/card/transactions? ${ params } ` ,
{
headers: {
'X-Client-ID' : clientId ,
'Authorization' : `Bearer ${ accessToken } `
}
}
);
// Returns: Uber transactions in October 2024 categorized as TRAVEL or FOOD
Transaction Response Fields
Core Transaction Fields
Unique transaction identifier
ID of card used for transaction
Last 4 digits of card number
Transaction timestamp (ISO 8601)
Transaction direction: DEBIT or CREDIT
DEBIT: Money leaving account (purchase)
CREDIT: Money entering account (refund, reversal)
Merchant name and location (e.g., “AMAZON.COM, SEATTLE”)
Type of merchant transaction (e.g., “OutOfWalletOnline”, “InStoreWithPin”)
Merchant Category Code (numeric code)
Human-readable category: SUBSCRIPTIONS, FOOD, TRAVEL, ENTERTAINMENT, HEALTH, ATM, UTILITIES, MISC
Financial Fields
Card currency (e.g., “EUR”, “USD”)
amountInTransactionCurrency
Amount charged to card (decimal string)
feesInTransactionCurrency
Fees charged in card currency (decimal string)
Merchant’s original currency
Original amount in merchant currency
Fees in original currency
Conversion rate used for billing
European Central Bank reference rate
Status Fields
Transaction status:
CONFIRMED: Transaction completed successfully
PENDING: Transaction processing
DECLINED: Transaction rejected
REVERTED: Transaction reversed/refunded
Reason for decline (only present when status: "DECLINED") Examples: “Insufficient funds”, “Invalid PIN”, “Card frozen”
Funding Source Fields
Array of crypto funding sources used for this transaction Show Funding Source Object
Funding source identifier
Wallet address that funded this transaction
Blockchain network: linea, solana, ethereum
Blockchain transaction hash
Cryptocurrency used (e.g., “usdc”, “usdt”)
Amount of crypto used (decimal string)
CONFIRMED, PENDING, or DECLINED
Funding transaction timestamp
Implementing Transaction History UI
Basic Transaction List
import { useState , useEffect } from 'react' ;
export function TransactionHistory ({ clientId , accessToken }) {
const [ transactions , setTransactions ] = useState ([]);
const [ loading , setLoading ] = useState ( true );
const [ error , setError ] = useState ( null );
const [ page , setPage ] = useState ( 0 );
useEffect (() => {
fetchTransactions ();
}, [ page ]);
async function fetchTransactions () {
setLoading ( true );
setError ( null );
try {
const response = await fetch (
`/v1/card/transactions?page= ${ page } ` ,
{
headers: {
'X-Client-ID' : clientId ,
'Authorization' : `Bearer ${ accessToken } `
}
}
);
if ( ! response . ok ) {
throw new Error ( 'Failed to fetch transactions' );
}
const data = await response . json ();
setTransactions ( data );
} catch ( err ) {
setError ( err . message );
} finally {
setLoading ( false );
}
}
if ( loading ) return < div > Loading transactions... </ div > ;
if ( error ) return < div > Error: { error } </ div > ;
return (
< div className = "transaction-history" >
< h2 > Transaction History </ h2 >
< div className = "transactions-list" >
{ transactions . map (( tx ) => (
< TransactionItem key = { tx . id } transaction = { tx } />
)) }
</ div >
< div className = "pagination" >
< button
onClick = { () => setPage ( Math . max ( 0 , page - 1 )) }
disabled = { page === 0 }
>
Previous
</ button >
< span > Page { page + 1 } </ span >
< button
onClick = { () => setPage ( page + 1 ) }
disabled = { transactions . length === 0 }
>
Next
</ button >
</ div >
</ div >
);
}
function TransactionItem ({ transaction }) {
const isCredit = transaction . sign === 'CREDIT' ;
const statusIcon = {
CONFIRMED: '✅' ,
PENDING: '⏳' ,
DECLINED: '❌' ,
REVERTED: '↩️'
}[ transaction . status ];
return (
< div className = "transaction-item" >
< div className = "transaction-merchant" >
< span className = "merchant-name" >
{ transaction . merchantNameLocation }
</ span >
< span className = "transaction-date" >
{new Date ( transaction . dateTime ). toLocaleDateString () }
</ span >
</ div >
< div className = "transaction-details" >
< span className = "category-badge" >
{ transaction . mccCategory }
</ span >
< span className = { `amount ${ isCredit ? 'credit' : 'debit' } ` } >
{ isCredit ? '+' : '-' }
{ transaction . transactionCurrency } { transaction . amountInTransactionCurrency }
</ span >
< span className = "status" > { statusIcon } </ span >
</ div >
</ div >
);
}
With Filters
React Component with Filters
import { useState , useEffect } from 'react' ;
export function TransactionHistoryWithFilters ({ clientId , accessToken }) {
const [ transactions , setTransactions ] = useState ([]);
const [ loading , setLoading ] = useState ( false );
const [ filters , setFilters ] = useState ({
dateFrom: '' ,
dateTo: '' ,
searchKey: '' ,
mccCategories: [],
page: 0
});
useEffect (() => {
fetchTransactions ();
}, [ filters ]);
async function fetchTransactions () {
setLoading ( true );
try {
const params = new URLSearchParams ();
if ( filters . dateFrom ) params . append ( 'dateFrom' , filters . dateFrom );
if ( filters . dateTo ) params . append ( 'dateTo' , filters . dateTo );
if ( filters . searchKey ) params . append ( 'searchKey' , filters . searchKey );
if ( filters . mccCategories . length > 0 ) {
params . append ( 'mccCategories' , filters . mccCategories . join ( ',' ));
}
params . append ( 'page' , filters . page . toString ());
const response = await fetch (
`/v1/card/transactions? ${ params } ` ,
{
headers: {
'X-Client-ID' : clientId ,
'Authorization' : `Bearer ${ accessToken } `
}
}
);
const data = await response . json ();
setTransactions ( data );
} catch ( err ) {
console . error ( 'Failed to fetch transactions:' , err );
} finally {
setLoading ( false );
}
}
function updateFilter ( key , value ) {
setFilters ( prev => ({ ... prev , [key]: value , page: 0 }));
}
function toggleCategory ( category ) {
setFilters ( prev => ({
... prev ,
mccCategories: prev . mccCategories . includes ( category )
? prev . mccCategories . filter ( c => c !== category )
: [ ... prev . mccCategories , category ],
page: 0
}));
}
const categories = [
'FOOD' , 'TRAVEL' , 'ENTERTAINMENT' , 'SUBSCRIPTIONS' ,
'HEALTH' , 'UTILITIES' , 'ATM' , 'MISC'
];
return (
< div className = "transaction-history-with-filters" >
< div className = "filters" >
< h3 > Filters </ h3 >
< div className = "date-range" >
< input
type = "date"
placeholder = "From"
value = { filters . dateFrom }
onChange = { ( e ) => updateFilter ( 'dateFrom' , e . target . value ) }
/>
< input
type = "date"
placeholder = "To"
value = { filters . dateTo }
onChange = { ( e ) => updateFilter ( 'dateTo' , e . target . value ) }
/>
</ div >
< div className = "search" >
< input
type = "text"
placeholder = "Search merchant..."
value = { filters . searchKey }
onChange = { ( e ) => updateFilter ( 'searchKey' , e . target . value ) }
/>
</ div >
< div className = "categories" >
{ categories . map ( category => (
< button
key = { category }
onClick = { () => toggleCategory ( category ) }
className = { filters . mccCategories . includes ( category ) ? 'active' : '' }
>
{ category }
</ button >
)) }
</ div >
< button onClick = { () => setFilters ({
dateFrom: '' , dateTo: '' , searchKey: '' ,
mccCategories: [], page: 0
}) } >
Clear Filters
</ button >
</ div >
< div className = "transactions" >
{ loading ? (
< div > Loading... </ div >
) : transactions . length > 0 ? (
transactions . map ( tx => (
< TransactionItem key = { tx . id } transaction = { tx } />
))
) : (
< div > No transactions found </ div >
) }
</ div >
</ div >
);
}
Transaction Statements
Generate downloadable transaction statements in CSV or PDF format.
Generate Statement
JavaScript/Node.js
Python
cURL (CSV)
// Generate CSV statement
const response = await fetch (
'https://dev.api.baanx.com/v1/card/transactions/statement?dateFrom=2024-10-01&dateTo=2024-10-31' ,
{
headers: {
'Accept' : 'text/csv' ,
'X-Client-ID' : clientId ,
'Authorization' : `Bearer ${ accessToken } `
}
}
);
const csvData = await response . text ();
// Download as file
const blob = new Blob ([ csvData ], { type: 'text/csv' });
const url = URL . createObjectURL ( blob );
const link = document . createElement ( 'a' );
link . href = url ;
link . download = 'transactions-october-2024.csv' ;
link . click ();
Statement Parameters
Start date for statement (ISO 8601). If omitted, includes all transactions from the beginning.
End date for statement (ISO 8601). If omitted, includes all transactions up to now.
Format for statement:
text/csv: CSV format
application/pdf: PDF format
Unlike the transactions endpoint, you can specify dateFrom without dateTo (or vice versa) for open-ended date ranges.
Timestamp, Merchant, Merchant Type, Transaction Currency, Transaction currency amount, Card currency, Card currency amount, Funding Tokens, Funding Addresses
2024-10-14T10:44:36.276Z, "WWW.ALIEXPRESS.COM, LONDON", OutOfWalletOnline, USD, 0.85, EUR, 0.79, "usdc", "0x3a11a86cf218c448be519728cd3ac5c741fb3424"
2024-10-13T15:22:11.143Z, "UBER TRIP, NEW YORK", OutOfWalletOnline, USD, 15.50, EUR, 14.32, "usdc", "0x3a11a86cf218c448be519728cd3ac5c741fb3424"
Statement Generation Component
import { useState } from 'react' ;
export function StatementGenerator ({ clientId , accessToken }) {
const [ loading , setLoading ] = useState ( false );
const [ dateFrom , setDateFrom ] = useState ( '' );
const [ dateTo , setDateTo ] = useState ( '' );
const [ format , setFormat ] = useState ( 'csv' );
async function generateStatement () {
setLoading ( true );
try {
const params = new URLSearchParams ();
if ( dateFrom ) params . append ( 'dateFrom' , dateFrom );
if ( dateTo ) params . append ( 'dateTo' , dateTo );
const response = await fetch (
`/v1/card/transactions/statement? ${ params } ` ,
{
headers: {
'Accept' : format === 'csv' ? 'text/csv' : 'application/pdf' ,
'X-Client-ID' : clientId ,
'Authorization' : `Bearer ${ accessToken } `
}
}
);
if ( ! response . ok ) {
throw new Error ( 'Failed to generate statement' );
}
// Download file
const blob = await response . blob ();
const url = URL . createObjectURL ( blob );
const link = document . createElement ( 'a' );
link . href = url ;
link . download = `statement- ${ dateFrom || 'all' } - ${ dateTo || 'latest' } . ${ format } ` ;
link . click ();
URL . revokeObjectURL ( url );
} catch ( err ) {
console . error ( 'Statement generation failed:' , err );
alert ( 'Failed to generate statement' );
} finally {
setLoading ( false );
}
}
return (
< div className = "statement-generator" >
< h3 > Generate Statement </ h3 >
< div className = "date-range" >
< label >
From:
< input
type = "date"
value = { dateFrom }
onChange = { ( e ) => setDateFrom ( e . target . value ) }
/>
</ label >
< label >
To:
< input
type = "date"
value = { dateTo }
onChange = { ( e ) => setDateTo ( e . target . value ) }
/>
</ label >
</ div >
< div className = "format-selector" >
< label >
< input
type = "radio"
value = "csv"
checked = { format === 'csv' }
onChange = { ( e ) => setFormat ( e . target . value ) }
/>
CSV
</ label >
< label >
< input
type = "radio"
value = "pdf"
checked = { format === 'pdf' }
onChange = { ( e ) => setFormat ( e . target . value ) }
/>
PDF
</ label >
</ div >
< button
onClick = { generateStatement }
disabled = { loading }
className = "btn-generate"
>
{ loading ? 'Generating...' : 'Download Statement' }
</ button >
{ ! dateFrom && ! dateTo && (
< p className = "info-text" >
No date range specified - will include all transactions
</ p >
) }
</ div >
);
}
Transaction Analytics
Spending by Category
function calculateSpendingByCategory ( transactions ) {
const spending = {};
transactions . forEach ( tx => {
if ( tx . status === 'CONFIRMED' && tx . sign === 'DEBIT' ) {
const category = tx . mccCategory ;
const amount = parseFloat ( tx . amountInTransactionCurrency );
spending [ category ] = ( spending [ category ] || 0 ) + amount ;
}
});
return spending ;
}
// Usage
const categorySpending = calculateSpendingByCategory ( transactions );
// { FOOD: 150.50, TRAVEL: 450.00, ENTERTAINMENT: 75.25, ... }
Monthly Spending Trend
function calculateMonthlySpending ( transactions ) {
const monthlySpend = {};
transactions . forEach ( tx => {
if ( tx . status === 'CONFIRMED' && tx . sign === 'DEBIT' ) {
const date = new Date ( tx . dateTime );
const monthKey = ` ${ date . getFullYear () } - ${ String ( date . getMonth () + 1 ). padStart ( 2 , '0' ) } ` ;
const amount = parseFloat ( tx . amountInTransactionCurrency );
monthlySpend [ monthKey ] = ( monthlySpend [ monthKey ] || 0 ) + amount ;
}
});
return monthlySpend ;
}
// Usage
const monthlyTrend = calculateMonthlySpending ( transactions );
// { '2024-10': 675.75, '2024-09': 542.30, ... }
Top Merchants
function getTopMerchants ( transactions , limit = 5 ) {
const merchantSpend = {};
transactions . forEach ( tx => {
if ( tx . status === 'CONFIRMED' && tx . sign === 'DEBIT' ) {
const merchant = tx . merchantNameLocation ;
const amount = parseFloat ( tx . amountInTransactionCurrency );
merchantSpend [ merchant ] = ( merchantSpend [ merchant ] || 0 ) + amount ;
}
});
return Object . entries ( merchantSpend )
. sort (([, a ], [, b ]) => b - a )
. slice ( 0 , limit )
. map (([ merchant , amount ]) => ({ merchant , amount }));
}
// Usage
const topMerchants = getTopMerchants ( transactions );
// [
// { merchant: 'AMAZON.COM, SEATTLE', amount: 350.00 },
// { merchant: 'UBER TRIP, NEW YORK', amount: 125.50 },
// ...
// ]
Troubleshooting
Empty array returned despite having transactions
Cause : Invalid page number or filters too restrictiveSolution : Reset to page 0 and verify filters// Start from first page
const response = await fetch ( '/v1/card/transactions?page=0' );
// Remove filters
const response = await fetch ( '/v1/card/transactions' );
Date range filter returns error
Cause : Only one of dateFrom/dateTo specifiedSolution : Provide both dates or neither// ❌ Bad: Only one date
? dateFrom = 2024 - 10 - 01
// ✅ Good: Both dates
? dateFrom = 2024 - 10 - 01 & dateTo = 2024 - 10 - 31
// ✅ Good: No dates (all transactions)
// (no date parameters)
Cause : Missing or incorrect Accept headerSolution : Set proper Accept header for desired format// For CSV
headers : {
'Accept' : 'text/csv'
}
// For PDF
headers : {
'Accept' : 'application/pdf'
}
Funding sources array is empty
Cause : Transaction not yet settled or failedSolution : Check transaction statusif ( transaction . status === 'PENDING' ) {
console . log ( 'Funding sources not yet finalized' );
} else if ( transaction . status === 'DECLINED' ) {
console . log ( 'Transaction declined - no funding occurred' );
}
Best Practices
Implement pagination for large transaction lists
Cache recent transactions locally with reasonable TTL
Use date filters to limit data transfer
Lazy load transaction details on demand
User Experience
Show loading states during fetch
Display transaction status clearly (confirmed, pending, declined)
Group transactions by date for readability
Provide search and filter capabilities
Allow statement downloads for record keeping
Data Display
Format amounts with proper decimal places
Show currency symbols clearly
Display timestamps in user’s timezone
Use color coding for transaction types (debit/credit)
Show funding source details for transparency
Error Handling
async function fetchTransactions ( filters ) {
try {
const response = await fetch ( buildTransactionUrl ( filters ));
if ( ! response . ok ) {
if ( response . status === 404 ) {
return []; // No transactions yet
}
throw new Error ( 'Failed to fetch transactions' );
}
return await response . json ();
} catch ( error ) {
console . error ( 'Transaction fetch error:' , error );
// Show user-friendly error message
throw error ;
}
}
Next Steps