List Balance Adjustments
Overview
Retrieve a list of balance adjustments for your application. View all top-ups and deductions to track balance changes, monitor adjustment history, and reconcile account activity.
Resource Access
- User Permissions: ROLE_PARTNER or ROLE_PLATFORM credentials only
- Endpoint:
GET /balance_adjustments
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | Number of items to return (default: 10) |
| after_cursor | string | No | Return items created after this cursor |
| before_cursor | string | No | Return items created before this cursor |
Example Requests
List All Balance Adjustments
curl -X GET \
'https://api.ahrvo.network/payments/na/balance_adjustments' \
-u username:password
Paginate with Limit
curl -X GET \
'https://api.ahrvo.network/payments/na/balance_adjustments?limit=50' \
-u username:password
Use Cursor Pagination
curl -X GET \
'https://api.ahrvo.network/payments/na/balance_adjustments?limit=100&after_cursor=balance_adjustment_jfR7V18CMWaGBmRYAto8Lw' \
-u username:password
Example Response
{
"_embedded": {
"balance_adjustments": [
{
"id": "balance_adjustment_jfR7V18CMWaGBmRYAto8Lw",
"created_at": "2024-05-22T17:13:15.810963Z",
"updated_at": "2024-05-22T17:13:16.05664Z",
"amount": 10000,
"balance_entry_id": "balance_entry_4Zsrw3ccS64vjpzZmNMdUi",
"currency": "USD",
"failure_code": null,
"failure_message": null,
"description": "Weekly balance top-up",
"rail": "ACH",
"instrument_id": "PIwyL8J2KRu8qnvGF7EDeviQ",
"state": "SUCCEEDED",
"type": "TOP_UP",
"tags": {
"purpose": "weekly_topup"
},
"trace_id": "872d2e13-2e56-47b6-94a3-64ec06a1f982",
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/balance_adjustments/balance_adjustment_jfR7V18CMWaGBmRYAto8Lw"
}
}
},
{
"id": "balance_adjustment_nvc873btpDtA7KbPySdCny",
"created_at": "2024-05-22T17:13:15.810963Z",
"updated_at": "2024-05-22T17:13:16.05664Z",
"amount": 5000,
"balance_entry_id": "balance_entry_xyz789",
"currency": "USD",
"failure_code": null,
"failure_message": null,
"description": "Platform fee deduction",
"rail": "ACH",
"instrument_id": "PIwyL8J2KRu8qnvGF7EDeviQ",
"state": "SUCCEEDED",
"type": "DEDUCTION",
"tags": {
"fee_type": "platform"
},
"trace_id": "872d2e13-2e56-47b6-94a3-64ec06a1f982",
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/balance_adjustments/balance_adjustment_nvc873btpDtA7KbPySdCny"
}
}
}
]
},
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/balance_adjustments"
}
},
"page": {
"limit": 100,
"next_cursor": null,
"offset": 0
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
| _embedded.balance_adjustments | array | Array of balance adjustment objects |
| _links | object | Navigation links |
| page | object | Pagination information |
Balance Adjustment Object Fields
| Field | Type | Description |
|---|---|---|
| id | string | Unique adjustment ID |
| created_at | string | Timestamp when created |
| updated_at | string | Timestamp when last updated |
| amount | integer | Adjustment amount in cents |
| balance_entry_id | string | Associated balance entry ID |
| currency | string | Currency code |
| failure_code | string | Failure code (if failed) |
| failure_message | string | Failure message (if failed) |
| description | string | Adjustment description |
| rail | string | Payment rail (ACH, WIRE) |
| instrument_id | string | Payment instrument ID |
| state | string | State (SUCCEEDED, FAILED, PENDING) |
| type | string | Type (TOP_UP, DEDUCTION) |
| tags | object | Metadata tags |
| trace_id | string | Trace ID for tracking |
Additional Information
-
Pagination: Use cursor-based pagination
- Default limit is 10
- Adjust limit based on needs
- Use
next_cursorfor next page - More efficient than offset pagination
-
Filtering: Currently limited
- No built-in filter parameters
- Retrieve all and filter client-side
- Use tags for categorization
- Consider date range filtering in code
-
Access Control: Platform/Partner only
- Only ROLE_PARTNER or ROLE_PLATFORM credentials
- Not accessible to merchant users
- Verify permissions before querying
-
Performance: Optimize queries
- Request appropriate page size
- Cache results when suitable
- Don't retrieve more than needed
Use Cases
List Recent Adjustments
// Get the most recent balance adjustments
async function getRecentAdjustments(limit = 20) {
const response = await fetch(
`https://api.ahrvo.network/payments/na/balance_adjustments?limit=${limit}`,
{
headers: {
'Authorization': 'Basic ' + btoa('username:password')
}
}
);
const data = await response.json();
console.log(`Found ${data._embedded.balance_adjustments.length} recent adjustments\n`);
data._embedded.balance_adjustments.forEach(adj => {
const sign = adj.type === 'TOP_UP' ? '+' : '-';
console.log(`${adj.id}:`);
console.log(` ${sign}$${adj.amount / 100} (${adj.type})`);
console.log(` State: ${adj.state}`);
console.log(` Created: ${adj.created_at}`);
console.log(` Description: ${adj.description}`);
});
return data._embedded.balance_adjustments;
}
Calculate Total Top-Ups
// Calculate total amount topped up
async function calculateTotalTopUps() {
let allAdjustments = [];
let cursor = null;
// Fetch all pages
do {
const url = cursor
? `https://api.ahrvo.network/payments/na/balance_adjustments?limit=100&after_cursor=${cursor}`
: 'https://api.ahrvo.network/payments/na/balance_adjustments?limit=100';
const response = await fetch(url, {
headers: {
'Authorization': 'Basic ' + btoa('username:password')
}
});
const data = await response.json();
allAdjustments.push(...data._embedded.balance_adjustments);
cursor = data.page.next_cursor;
} while (cursor);
// Calculate totals
const topUps = allAdjustments.filter(adj => adj.type === 'TOP_UP' && adj.state === 'SUCCEEDED');
const totalTopUps = topUps.reduce((sum, adj) => sum + adj.amount, 0);
console.log('=== Top-Up Summary ===');
console.log(`Total Top-Ups: ${topUps.length}`);
console.log(`Total Amount: $${totalTopUps / 100}`);
console.log(`Average: $${(totalTopUps / topUps.length / 100).toFixed(2)}`);
return {
count: topUps.length,
total: totalTopUps,
average: totalTopUps / topUps.length
};
}
Filter by Type
// Get adjustments of specific type
async function getAdjustmentsByType(type) {
const response = await fetch(
'https://api.ahrvo.network/payments/na/balance_adjustments?limit=100',
{
headers: {
'Authorization': 'Basic ' + btoa('username:password')
}
}
);
const data = await response.json();
const filtered = data._embedded.balance_adjustments.filter(
adj => adj.type === type
);
console.log(`Found ${filtered.length} ${type} adjustments:`);
filtered.forEach(adj => {
console.log(` ${adj.id}: $${adj.amount / 100} - ${adj.description}`);
});
return filtered;
}
// Usage
await getAdjustmentsByType('TOP_UP');
await getAdjustmentsByType('DEDUCTION');
Find Failed Adjustments
// Identify and analyze failed adjustments
async function findFailedAdjustments() {
const response = await fetch(
'https://api.ahrvo.network/payments/na/balance_adjustments?limit=100',
{
headers: {
'Authorization': 'Basic ' + btoa('username:password')
}
}
);
const data = await response.json();
const failed = data._embedded.balance_adjustments.filter(
adj => adj.state === 'FAILED'
);
if (failed.length === 0) {
console.log('✓ No failed adjustments found');
return [];
}
console.log(`⚠ Found ${failed.length} failed adjustments:\n`);
failed.forEach(adj => {
console.log(`${adj.id}:`);
console.log(` Type: ${adj.type}`);
console.log(` Amount: $${adj.amount / 100}`);
console.log(` Failure: ${adj.failure_code} - ${adj.failure_message}`);
console.log(` Created: ${adj.created_at}`);
});
return failed;
}
Generate Monthly Report
// Generate monthly adjustment report
async function monthlyAdjustmentReport(year, month) {
console.log(`=== Monthly Adjustment Report ===`);
console.log(`Period: ${year}-${month.toString().padStart(2, '0')}\n`);
// Fetch all adjustments
let allAdjustments = [];
let cursor = null;
do {
const url = cursor
? `https://api.ahrvo.network/payments/na/balance_adjustments?limit=100&after_cursor=${cursor}`
: 'https://api.ahrvo.network/payments/na/balance_adjustments?limit=100';
const response = await fetch(url, {
headers: {
'Authorization': 'Basic ' + btoa('username:password')
}
});
const data = await response.json();
allAdjustments.push(...data._embedded.balance_adjustments);
cursor = data.page.next_cursor;
} while (cursor);
// Filter for specified month
const monthlyAdjustments = allAdjustments.filter(adj => {
const created = new Date(adj.created_at);
return created.getFullYear() === year && created.getMonth() + 1 === month;
});
// Calculate metrics
const succeeded = monthlyAdjustments.filter(a => a.state === 'SUCCEEDED');
const topUps = succeeded.filter(a => a.type === 'TOP_UP');
const deductions = succeeded.filter(a => a.type === 'DEDUCTION');
const totalTopUps = topUps.reduce((sum, a) => sum + a.amount, 0);
const totalDeductions = deductions.reduce((sum, a) => sum + a.amount, 0);
const netChange = totalTopUps - totalDeductions;
console.log('Summary:');
console.log(` Total Adjustments: ${monthlyAdjustments.length}`);
console.log(` Successful: ${succeeded.length}`);
console.log(`\nTop-Ups:`);
console.log(` Count: ${topUps.length}`);
console.log(` Total: $${totalTopUps / 100}`);
console.log(`\nDeductions:`);
console.log(` Count: ${deductions.length}`);
console.log(` Total: $${totalDeductions / 100}`);
console.log(`\nNet Change: $${netChange / 100}`);
// Breakdown by rail
const achCount = succeeded.filter(a => a.rail === 'ACH').length;
const wireCount = succeeded.filter(a => a.rail === 'WIRE').length;
console.log(`\nBy Rail:`);
console.log(` ACH: ${achCount}`);
console.log(` WIRE: ${wireCount}`);
return {
total: monthlyAdjustments.length,
succeeded: succeeded.length,
topUps: { count: topUps.length, total: totalTopUps },
deductions: { count: deductions.length, total: totalDeductions },
netChange: netChange,
byRail: { ACH: achCount, WIRE: wireCount }
};
}
// Usage
await monthlyAdjustmentReport(2024, 12);
Export to CSV
// Export balance adjustments to CSV
async function exportAdjustmentsToCSV() {
console.log('Fetching all balance adjustments...');
let allAdjustments = [];
let cursor = null;
do {
const url = cursor
? `https://api.ahrvo.network/payments/na/balance_adjustments?limit=100&after_cursor=${cursor}`
: 'https://api.ahrvo.network/payments/na/balance_adjustments?limit=100';
const response = await fetch(url, {
headers: {
'Authorization': 'Basic ' + btoa('username:password')
}
});
const data = await response.json();
allAdjustments.push(...data._embedded.balance_adjustments);
cursor = data.page.next_cursor;
} while (cursor);
// Create CSV
let csv = 'ID,Created,Type,Amount,Currency,State,Rail,Description\n';
allAdjustments.forEach(adj => {
const amount = adj.type === 'DEDUCTION' ? -adj.amount : adj.amount;
csv += `${adj.id},${adj.created_at},${adj.type},${amount / 100},${adj.currency},${adj.state},${adj.rail},"${adj.description}"\n`;
});
console.log(`✓ Exported ${allAdjustments.length} adjustments to CSV`);
return csv;
}
Track Pending Adjustments
// Monitor pending adjustments
async function trackPendingAdjustments() {
const response = await fetch(
'https://api.ahrvo.network/payments/na/balance_adjustments?limit=100',
{
headers: {
'Authorization': 'Basic ' + btoa('username:password')
}
}
);
const data = await response.json();
const pending = data._embedded.balance_adjustments.filter(
adj => adj.state === 'PENDING'
);
if (pending.length === 0) {
console.log('✓ No pending adjustments');
return [];
}
console.log(`Found ${pending.length} pending adjustments:\n`);
pending.forEach(adj => {
const created = new Date(adj.created_at);
const hoursAgo = Math.floor((Date.now() - created.getTime()) / (1000 * 60 * 60));
console.log(`${adj.id}:`);
console.log(` Type: ${adj.type}`);
console.log(` Amount: $${adj.amount / 100}`);
console.log(` Rail: ${adj.rail}`);
console.log(` Created: ${hoursAgo} hours ago`);
if (adj.rail === 'ACH' && hoursAgo > 72) {
console.log(` ⚠ WARNING: ACH pending for over 72 hours`);
} else if (adj.rail === 'WIRE' && hoursAgo > 24) {
console.log(` ⚠ WARNING: WIRE pending for over 24 hours`);
}
});
return pending;
}
Best Practices
-
Paginate Efficiently: Don't fetch everything
// Good - use appropriate limit
const data = await fetchAdjustments({ limit: 50 });
// Bad - fetch all unnecessarily
const all = await fetchAllAdjustments(); // Could be thousands -
Cache When Appropriate: Reduce API calls
const cache = {
data: null,
timestamp: null,
ttl: 5 * 60 * 1000 // 5 minutes
};
async function getCachedAdjustments() {
if (cache.data && Date.now() - cache.timestamp < cache.ttl) {
return cache.data;
}
const data = await fetchAdjustments();
cache.data = data;
cache.timestamp = Date.now();
return data;
} -
Filter Client-Side: No server-side filters
- Fetch with reasonable limit
- Filter by type, state, date in code
- Cache filtered results
-
Monitor Failed Adjustments: Regular checks
- Check for failed adjustments daily
- Investigate failure reasons
- Retry or escalate as needed
-
Track Net Changes: Balance reconciliation
- Sum top-ups and deductions
- Calculate net balance change
- Compare with balance API
- Identify discrepancies
Common Workflows
Daily Adjustment Summary
// Generate daily summary of adjustments
async function dailyAdjustmentSummary() {
const response = await fetch(
'https://api.ahrvo.network/payments/na/balance_adjustments?limit=100',
{
headers: {
'Authorization': 'Basic ' + btoa('username:password')
}
}
);
const data = await response.json();
// Filter for today
const today = new Date();
today.setHours(0, 0, 0, 0);
const todaysAdjustments = data._embedded.balance_adjustments.filter(adj => {
const created = new Date(adj.created_at);
return created >= today;
});
console.log('=== Daily Adjustment Summary ===');
console.log(`Date: ${today.toDateString()}\n`);
const succeeded = todaysAdjustments.filter(a => a.state === 'SUCCEEDED');
const pending = todaysAdjustments.filter(a => a.state === 'PENDING');
const failed = todaysAdjustments.filter(a => a.state === 'FAILED');
console.log(`Total: ${todaysAdjustments.length}`);
console.log(` Succeeded: ${succeeded.length}`);
console.log(` Pending: ${pending.length}`);
console.log(` Failed: ${failed.length}`);
// Calculate amounts
const topUps = succeeded.filter(a => a.type === 'TOP_UP');
const deductions = succeeded.filter(a => a.type === 'DEDUCTION');
const totalTopUp = topUps.reduce((sum, a) => sum + a.amount, 0);
const totalDeduction = deductions.reduce((sum, a) => sum + a.amount, 0);
console.log(`\nTop-Ups: $${totalTopUp / 100} (${topUps.length})`);
console.log(`Deductions: $${totalDeduction / 100} (${deductions.length})`);
console.log(`Net: $${(totalTopUp - totalDeduction) / 100}`);
return {
total: todaysAdjustments.length,
succeeded: succeeded.length,
pending: pending.length,
failed: failed.length,
totalTopUp,
totalDeduction
};
}
Reconciliation Report
// Reconcile adjustments with balance
async function reconcileAdjustments() {
console.log('=== Balance Reconciliation ===\n');
// Fetch all adjustments
let allAdjustments = [];
let cursor = null;
do {
const url = cursor
? `https://api.ahrvo.network/payments/na/balance_adjustments?limit=100&after_cursor=${cursor}`
: 'https://api.ahrvo.network/payments/na/balance_adjustments?limit=100';
const response = await fetch(url, {
headers: {
'Authorization': 'Basic ' + btoa('username:password')
}
});
const data = await response.json();
allAdjustments.push(...data._embedded.balance_adjustments);
cursor = data.page.next_cursor;
} while (cursor);
// Calculate total adjustments
const succeeded = allAdjustments.filter(a => a.state === 'SUCCEEDED');
const totalTopUps = succeeded
.filter(a => a.type === 'TOP_UP')
.reduce((sum, a) => sum + a.amount, 0);
const totalDeductions = succeeded
.filter(a => a.type === 'DEDUCTION')
.reduce((sum, a) => sum + a.amount, 0);
const netAdjustments = totalTopUps - totalDeductions;
console.log('Adjustment History:');
console.log(` Total Top-Ups: $${totalTopUps / 100}`);
console.log(` Total Deductions: $${totalDeductions / 100}`);
console.log(` Net Adjustments: $${netAdjustments / 100}`);
return {
totalTopUps,
totalDeductions,
netAdjustments
};
}
Security Considerations
-
Access Control: Platform/Partner only
- Only specific roles can list adjustments
- Verify credentials have proper permissions
- Not accessible to merchant users
-
Sensitive Data: Handle carefully
- Adjustments contain financial data
- Don't expose to unauthorized users
- Log access to adjustment data
- Implement audit trails
-
Rate Limiting: Respect limits
- Don't poll excessively
- Cache results appropriately
- Use webhooks if available
Error Responses
Unauthorized
{
"_embedded": {
"errors": [
{
"message": "Authentication credentials are invalid"
}
]
}
}
Forbidden
{
"_embedded": {
"errors": [
{
"message": "User does not have permission to perform this action"
}
]
}
}
Troubleshooting
No Results Returned
- Verify you have ROLE_PARTNER or ROLE_PLATFORM credentials
- Check application has balance adjustments
- Try without pagination parameters first
- Verify environment (staging vs production)
Pagination Not Working
- Verify cursor value is valid
- Ensure cursor is from previous response
- Check cursor hasn't expired
- Start from beginning if issues
Missing Recent Adjustments
- Results are eventually consistent
- New adjustments may take moments to appear
- Refresh and try again
- Check creation timestamp
Related Endpoints
- POST /balance_adjustments: Create new adjustment
- GET /balances: Check current balance
- GET /balance_entries: View balance ledger
- GET /payment_instruments/{id}: View payment instrument