Query Budget Transfer Order
Overview
Use this endpoint to query the status of a specific transfer between two budget accounts. This allows you to verify whether a transfer was successful or failed, which is essential for reconciliation and error handling in your budget management workflows.
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/budget/v2/transfer/order
Staging (gateway.ahrvo.network)
GET https://gateway.ahrvo.network/card/issuance/api/issuing/budget/v2/transfer/order
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 |
|---|---|---|---|
transfer_order_id | string | Yes | A unique ID for the fund transfer order (received from the transfer initiation response) |
Response
Success Response (200 OK)
{
"code": "SUCCESS",
"data": {
"fund_result": "SUCCESS"
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
code | string | Status string indicating the result of the query. "SUCCESS" refers to a successful query |
data | object | Response data object |
data.fund_result | string | The processing status of the transfer order. "SUCCESS" indicates the transfer was successful, "FAILED" indicates it failed |
Transfer Status Values
| Status | Description |
|---|---|
SUCCESS | The transfer was completed successfully |
FAILED | The transfer failed (insufficient funds, invalid accounts, etc.) |
Error Responses
- 400 Bad Request: Invalid or missing
transfer_order_id - 401 Unauthorized: Invalid or missing authentication token
- 403 Forbidden: Card issuance feature not enabled for this account
- 404 Not Found: Transfer order does not exist
Code Examples
cURL
curl -X GET \
'https://gateway.ahrvo.network/card/issuance/api/issuing/budget/v2/transfer/order?transfer_order_id=toi202311021736413952' \
-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/budget/v2/transfer/order"
headers = {
"Accept": "application/json",
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"x-api-key": "YOUR_API_KEY"
}
params = {
"transfer_order_id": "toi202311021736413952"
}
response = requests.get(url, headers=headers, params=params)
result = response.json()
if result['code'] == 'SUCCESS':
transfer_status = result['data']['fund_result']
if transfer_status == 'SUCCESS':
print("✓ Transfer completed successfully")
elif transfer_status == 'FAILED':
print("✗ Transfer failed")
else:
print(f"Transfer status: {transfer_status}")
else:
print(f"Failed to query transfer order: {result}")
JavaScript (Node.js)
const axios = require('axios');
const url = 'https://gateway.ahrvo.network/card/issuance/api/issuing/budget/v2/transfer/order';
const headers = {
'Accept': 'application/json',
'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
'x-api-key': 'YOUR_API_KEY'
};
const params = {
transfer_order_id: 'toi202311021736413952'
};
axios.get(url, { headers, params })
.then(response => {
const result = response.data;
if (result.code === 'SUCCESS') {
const transferStatus = result.data.fund_result;
if (transferStatus === 'SUCCESS') {
console.log('✓ Transfer completed successfully');
} else if (transferStatus === 'FAILED') {
console.log('✗ Transfer failed');
} else {
console.log(`Transfer status: ${transferStatus}`);
}
}
})
.catch(error => {
console.error('Failed to query transfer order:', error.response.data);
});
Usage Notes
- Transfer Order ID: You receive this ID when you initiate a transfer between budget accounts
- Asynchronous Processing: Transfers may be processed asynchronously, so query the status to confirm completion
- Status Check: Always verify the transfer status before proceeding with dependent operations
- Idempotent: You can query the same transfer order multiple times safely
- Reconciliation: Use this endpoint for reconciliation and audit purposes
Common Use Cases
Verify Transfer After Initiation
Check if a transfer completed successfully after initiating it:
def verify_transfer_completion(transfer_order_id, max_attempts=5, wait_seconds=2):
"""
Verify a transfer has completed successfully with retry logic
"""
import time
for attempt in range(max_attempts):
try:
response = query_budget_transfer_order(transfer_order_id)
if response['code'] == 'SUCCESS':
fund_result = response['data']['fund_result']
if fund_result == 'SUCCESS':
print(f"✓ Transfer {transfer_order_id} completed successfully")
return True
elif fund_result == 'FAILED':
print(f"✗ Transfer {transfer_order_id} failed")
return False
# Wait before retrying
if attempt < max_attempts - 1:
print(f"Attempt {attempt + 1}/{max_attempts}: Transfer still processing, waiting {wait_seconds}s...")
time.sleep(wait_seconds)
except Exception as e:
print(f"Error checking transfer status: {e}")
if attempt < max_attempts - 1:
time.sleep(wait_seconds)
print(f"⚠ Could not verify transfer status after {max_attempts} attempts")
return None
# Example usage
transfer_order_id = "toi202311021736413952"
success = verify_transfer_completion(transfer_order_id)
Transfer with Confirmation
Initiate a transfer and wait for confirmation:
async function transferWithConfirmation(fromBudgetId, toBudgetId, amount) {
try {
// Step 1: Initiate transfer
console.log('Initiating transfer...');
const transferResponse = await initiateTransfer({
from_budget_id: fromBudgetId,
to_budget_id: toBudgetId,
amount: amount
});
const transferOrderId = transferResponse.data.transfer_order_id;
console.log(`Transfer initiated: ${transferOrderId}`);
// Step 2: Wait and verify completion
console.log('Verifying transfer completion...');
const maxAttempts = 5;
const delayMs = 2000;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
await new Promise(resolve => setTimeout(resolve, delayMs));
const statusResponse = await queryBudgetTransferOrder(transferOrderId);
if (statusResponse.code === 'SUCCESS') {
const fundResult = statusResponse.data.fund_result;
if (fundResult === 'SUCCESS') {
console.log('✓ Transfer completed successfully');
return {
success: true,
transfer_order_id: transferOrderId
};
} else if (fundResult === 'FAILED') {
console.log('✗ Transfer failed');
return {
success: false,
transfer_order_id: transferOrderId,
error: 'Transfer failed'
};
}
}
console.log(`Attempt ${attempt}/${maxAttempts}: Still processing...`);
}
console.log('⚠ Transfer status could not be confirmed');
return {
success: null,
transfer_order_id: transferOrderId,
error: 'Status verification timeout'
};
} catch (error) {
console.error('Transfer with confirmation failed:', error);
throw error;
}
}
// Example usage
const result = await transferWithConfirmation(
'ci202507232016182661',
'ci202507232016182662',
5000.00
);
Batch Transfer Verification
Verify multiple transfers at once:
def verify_batch_transfers(transfer_order_ids):
"""
Verify the status of multiple transfers
"""
results = {
'successful': [],
'failed': [],
'pending': []
}
for transfer_id in transfer_order_ids:
try:
response = query_budget_transfer_order(transfer_id)
if response['code'] == 'SUCCESS':
fund_result = response['data']['fund_result']
if fund_result == 'SUCCESS':
results['successful'].append(transfer_id)
elif fund_result == 'FAILED':
results['failed'].append(transfer_id)
else:
results['pending'].append(transfer_id)
except Exception as e:
print(f"Error checking transfer {transfer_id}: {e}")
results['pending'].append(transfer_id)
# Print summary
print("\n=== Transfer Verification Summary ===")
print(f"Total Transfers: {len(transfer_order_ids)}")
print(f"Successful: {len(results['successful'])}")
print(f"Failed: {len(results['failed'])}")
print(f"Pending/Unknown: {len(results['pending'])}")
return results
# Example usage
transfer_ids = [
"toi202311021736413952",
"toi202311021736413953",
"toi202311021736413954"
]
verification_results = verify_batch_transfers(transfer_ids)
Reconciliation Report
Generate a reconciliation report for transfers:
async function generateTransferReconciliationReport(transferOrders) {
const report = {
generated_at: new Date().toISOString(),
total_transfers: transferOrders.length,
successful_count: 0,
failed_count: 0,
total_amount_successful: 0,
total_amount_failed: 0,
details: []
};
for (const order of transferOrders) {
try {
const response = await queryBudgetTransferOrder(order.transfer_order_id);
if (response.code === 'SUCCESS') {
const status = response.data.fund_result;
const detail = {
transfer_order_id: order.transfer_order_id,
from_budget: order.from_budget_name,
to_budget: order.to_budget_name,
amount: order.amount,
currency: order.currency,
status: status,
initiated_at: order.initiated_at
};
if (status === 'SUCCESS') {
report.successful_count++;
report.total_amount_successful += order.amount;
} else if (status === 'FAILED') {
report.failed_count++;
report.total_amount_failed += order.amount;
}
report.details.push(detail);
}
} catch (error) {
console.error(`Error checking transfer ${order.transfer_order_id}:`, error);
}
}
// Print report
console.log('\n=== Transfer Reconciliation Report ===');
console.log(`Generated: ${report.generated_at}`);
console.log(`Total Transfers: ${report.total_transfers}`);
console.log(`Successful: ${report.successful_count} (${report.total_amount_successful.toFixed(2)} USD)`);
console.log(`Failed: ${report.failed_count} (${report.total_amount_failed.toFixed(2)} USD)`);
console.log('\nDetails:');
report.details.forEach(detail => {
const statusIcon = detail.status === 'SUCCESS' ? '✓' : '✗';
console.log(`${statusIcon} ${detail.transfer_order_id}: ${detail.from_budget} → ${detail.to_budget} (${detail.currency} ${detail.amount})`);
});
return report;
}
// Example usage
const transfers = [
{
transfer_order_id: 'toi202311021736413952',
from_budget_name: 'Marketing',
to_budget_name: 'Operations',
amount: 5000,
currency: 'USD',
initiated_at: '2026-02-07T10:30:00Z'
},
// ... more transfers
];
const report = await generateTransferReconciliationReport(transfers);
Automated Retry on Failed Transfer
Handle failed transfers with automated retry:
def transfer_with_retry(from_budget_id, to_budget_id, amount, max_retries=3):
"""
Initiate transfer with automatic retry on failure
"""
for attempt in range(max_retries):
try:
# Initiate transfer
transfer_response = initiate_budget_transfer(
from_budget_id,
to_budget_id,
amount
)
transfer_order_id = transfer_response['data']['transfer_order_id']
print(f"Transfer initiated (Attempt {attempt + 1}): {transfer_order_id}")
# Wait for processing
time.sleep(3)
# Check status
status_response = query_budget_transfer_order(transfer_order_id)
if status_response['code'] == 'SUCCESS':
fund_result = status_response['data']['fund_result']
if fund_result == 'SUCCESS':
print(f"✓ Transfer successful on attempt {attempt + 1}")
return {
'success': True,
'transfer_order_id': transfer_order_id,
'attempts': attempt + 1
}
elif fund_result == 'FAILED':
print(f"✗ Transfer failed on attempt {attempt + 1}")
if attempt < max_retries - 1:
print(f"Retrying... ({max_retries - attempt - 1} attempts remaining)")
time.sleep(5) # Wait longer before retry
else:
return {
'success': False,
'transfer_order_id': transfer_order_id,
'attempts': attempt + 1,
'error': 'Transfer failed after all retries'
}
except Exception as e:
print(f"Error on attempt {attempt + 1}: {e}")
if attempt < max_retries - 1:
time.sleep(5)
else:
return {
'success': False,
'attempts': attempt + 1,
'error': str(e)
}
return {
'success': False,
'attempts': max_retries,
'error': 'Max retries exceeded'
}
# Example usage
result = transfer_with_retry('ci202507232016182661', 'ci202507232016182662', 5000.00)
Transfer Monitoring Dashboard
Monitor ongoing transfers:
class TransferMonitor {
constructor() {
this.pendingTransfers = new Map();
}
async addTransfer(transferOrderId, metadata = {}) {
this.pendingTransfers.set(transferOrderId, {
...metadata,
added_at: new Date(),
last_checked: null,
status: 'PENDING'
});
console.log(`Added transfer to monitor: ${transferOrderId}`);
}
async checkAllTransfers() {
console.log(`\nChecking ${this.pendingTransfers.size} pending transfers...`);
const completedTransfers = [];
for (const [transferId, info] of this.pendingTransfers.entries()) {
try {
const response = await queryBudgetTransferOrder(transferId);
if (response.code === 'SUCCESS') {
const status = response.data.fund_result;
info.last_checked = new Date();
info.status = status;
if (status === 'SUCCESS' || status === 'FAILED') {
console.log(` ${status === 'SUCCESS' ? '✓' : '✗'} ${transferId}: ${status}`);
completedTransfers.push(transferId);
} else {
console.log(` ⏳ ${transferId}: Still processing`);
}
}
} catch (error) {
console.error(` Error checking ${transferId}:`, error.message);
}
}
// Remove completed transfers
completedTransfers.forEach(id => {
console.log(`Removing completed transfer: ${id}`);
this.pendingTransfers.delete(id);
});
return {
checked: this.pendingTransfers.size + completedTransfers.length,
completed: completedTransfers.length,
pending: this.pendingTransfers.size
};
}
getPendingCount() {
return this.pendingTransfers.size;
}
getPendingTransfers() {
return Array.from(this.pendingTransfers.entries()).map(([id, info]) => ({
transfer_order_id: id,
...info
}));
}
}
// Example usage
const monitor = new TransferMonitor();
// Add transfers to monitor
await monitor.addTransfer('toi202311021736413952', {
amount: 5000,
from: 'Marketing',
to: 'Operations'
});
// Check periodically
setInterval(async () => {
if (monitor.getPendingCount() > 0) {
const results = await monitor.checkAllTransfers();
console.log(`\nMonitoring Summary: ${results.pending} pending, ${results.completed} completed`);
}
}, 5000); // Check every 5 seconds
Best Practices
- Always Verify: Check transfer status after initiating a transfer
- Retry Logic: Implement retry logic with exponential backoff for queries
- Store Transfer IDs: Save transfer order IDs for reconciliation and audit purposes
- Timeout Handling: Set reasonable timeouts for status verification
- Error Handling: Handle all possible status values and error cases
- Reconciliation: Regularly reconcile transfers with your internal records
Transfer Workflow
Standard Transfer Process
- Initiate Transfer: Call the transfer endpoint to move funds between budgets
- Receive Transfer Order ID: Store the returned
transfer_order_id - Wait for Processing: Allow time for the transfer to process (typically seconds)
- Query Status: Use this endpoint to check the transfer status
- Handle Result: Take appropriate action based on SUCCESS or FAILED status
- Update Records: Update your internal systems with the final status
Related Endpoints
- Transfer Between Budgets - Initiate a transfer between two budget accounts
- Query Budget Balance - Check available balance before and after transfers
- Create Budget Account - Create budget accounts for transfers
- Query Budget Details - Get detailed information about budget accounts
Troubleshooting
Transfer Order Not Found (404)
- Cause: Invalid
transfer_order_idor transfer doesn't exist - Solution:
- Verify the
transfer_order_idis correct - Ensure you're using the ID from a valid transfer initiation
- Verify the
Transfer Status Remains Pending
- Cause: Transfer is still processing or stuck
- Solution:
- Wait longer and retry the query
- Check with support if status doesn't update after several minutes
- Verify source budget has sufficient funds
Failed Transfer Status
- Cause: Transfer failed due to insufficient funds, invalid budgets, or other issues
- Solution:
- Check source budget balance
- Verify both budget IDs exist
- Review transfer amount and currency
- Contact support if issue persists
Security Considerations
- Transfer Order ID Security: Treat transfer order IDs as sensitive information
- Audit Logging: Log all transfer status queries for audit purposes
- Access Control: Implement proper authorization before allowing status queries
- Data Retention: Maintain transfer records for compliance requirements