Skip to main content

List Transfer Reversals

Overview

Retrieve all Reversals (refunds) for a specific Transfer. Track refund history, calculate total refunded, and verify refund status.

Resource Access

  • User Permissions: Admin users only
  • Endpoint: GET /transfers/\{transfer_id}/reversals

Arguments

ParameterTypeRequiredDescription
transfer_idstringYesID of original Transfer (in URL path)

Example Request

curl -X GET \
'https://api.ahrvo.network/payments/na/transfers/TRtransfer789/reversals' \
-u username:password

Example Response (Multiple Reversals)

{
"_embedded": {
"transfers": [
{
"id": "TRreversal123",
"created_at": "2023-12-11T15:30:00Z",
"updated_at": "2023-12-11T15:30:10Z",
"amount": 3000,
"currency": "USD",
"state": "SUCCEEDED",
"type": "REVERSAL",
"merchant": "MUmerchant123",
"source": null,
"destination": "PIcreditCard456",
"fee": 0,
"ready_to_settle_at": "2023-12-11T15:30:10Z",
"trace_id": "TRC_98765435",
"parent_transfer": "TRtransfer789",
"tags": {
"reason": "partial_return",
"items_returned": "1",
"refund_type": "partial"
},
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/transfers/TRreversal123"
},
"parent": {
"href": "https://api.ahrvo.network/payments/na/transfers/TRtransfer789"
}
}
},
{
"id": "TRreversal124",
"created_at": "2023-12-12T10:15:00Z",
"updated_at": "2023-12-12T10:15:08Z",
"amount": 2000,
"currency": "USD",
"state": "SUCCEEDED",
"type": "REVERSAL",
"merchant": "MUmerchant123",
"source": null,
"destination": "PIcreditCard456",
"fee": 0,
"ready_to_settle_at": "2023-12-12T10:15:08Z",
"trace_id": "TRC_98765436",
"parent_transfer": "TRtransfer789",
"tags": {
"reason": "partial_return",
"items_returned": "1",
"refund_type": "partial"
},
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/transfers/TRreversal124"
},
"parent": {
"href": "https://api.ahrvo.network/payments/na/transfers/TRtransfer789"
}
}
}
]
},
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/transfers/TRtransfer789/reversals"
},
"parent": {
"href": "https://api.ahrvo.network/payments/na/transfers/TRtransfer789"
}
},
"page": {
"limit": 20,
"offset": 0,
"count": 2
}
}

Example Response (No Reversals)

{
"_embedded": {
"transfers": []
},
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/transfers/TRtransfer790/reversals"
},
"parent": {
"href": "https://api.ahrvo.network/payments/na/transfers/TRtransfer790"
}
},
"page": {
"limit": 20,
"offset": 0,
"count": 0
}
}

Example Response (Pending Reversal)

{
"_embedded": {
"transfers": [
{
"id": "TRreversal125",
"created_at": "2023-12-13T14:00:00Z",
"updated_at": "2023-12-13T14:00:00Z",
"amount": 10000,
"currency": "USD",
"state": "PENDING",
"type": "REVERSAL",
"merchant": "MUmerchant123",
"source": null,
"destination": "PIcreditCard456",
"fee": 0,
"ready_to_settle_at": null,
"trace_id": "TRC_98765437",
"parent_transfer": "TRtransfer791",
"tags": {
"reason": "customer_request",
"refund_type": "full"
},
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/transfers/TRreversal125"
},
"parent": {
"href": "https://api.ahrvo.network/payments/na/transfers/TRtransfer791"
}
}
}
]
},
"_links": {
"self": {
"href": "https://api.ahrvo.network/payments/na/transfers/TRtransfer791/reversals"
},
"parent": {
"href": "https://api.ahrvo.network/payments/na/transfers/TRtransfer791"
}
},
"page": {
"limit": 20,
"offset": 0,
"count": 1
}
}

Additional Information

  • Reversals are Transfers: Each reversal is a full Transfer object
    • Has own Transfer ID (starts with "TR")
    • Type = "REVERSAL"
    • Can be fetched individually via GET /transfers/{reversal_id}
    • Appears in GET /transfers list
    • Has all standard Transfer fields
  • Parent Transfer: Links back to original
    • parent_transfer field contains original Transfer ID
    • Same for all reversals of one transfer
    • Use to group refunds
  • Multiple Reversals: Possible
    • Can refund same transfer multiple times
    • Each appears in list
    • Sum amounts to see total refunded
    • Example: $100 charge → $30 refund + $20 refund = $50 total
  • States: Check reversal status
    • PENDING: Refund submitted, processing
      • Just created
      • Awaiting processor
      • Should succeed within minutes to hours
    • SUCCEEDED: Refund processed
      • Funds returned to customer
      • Will appear in customer's account in 5-10 days
      • Ready to settle (deducted from merchant)
    • FAILED: Refund failed (rare)
      • Card closed, expired, etc.
      • Check messages for details
      • May need alternative refund method
  • Amount: Refund amount
    • In cents
    • May be partial refund
    • Sum all reversals to get total refunded
    • Cannot exceed original transfer amount
  • Fee: Always 0 for reversals
    • No processing fee on refunds
    • Original transfer fee NOT returned
    • Merchant loses original fee
  • Destination: Customer's payment instrument
    • Same as original source
    • Where refund goes
    • Customer's card or bank account
  • Source: Null for reversals
    • Not charging anyone
    • Funds come from merchant
    • Opposite of original transfer
  • Empty List: No refunds
    • _embedded.transfers is empty array
    • page.count is 0
    • Transfer never refunded
    • Normal for most transactions
  • Chronological Order: Newest first
    • Most recent reversal at top
    • Ordered by creation date
    • Cannot change sort order
  • No Pagination: Typically few reversals
    • Most transfers have 0-1 reversals
    • Rare to have many
    • Full list returned
    • No need for cursors

Use Cases

Calculate Total Refunded

// Get all reversals
const response = await listReversals('TRtransfer789');
const reversals = response._embedded.transfers;

// Sum successful refunds only
const totalRefunded = reversals
.filter(r => r.state === 'SUCCEEDED')
.reduce((sum, r) => sum + r.amount, 0);

// Get original transfer
const transfer = await fetchTransfer('TRtransfer789');

// Calculate remaining
const remainingRefundable = transfer.amount - totalRefunded;

console.log(`Original amount: $${transfer.amount / 100}`);
console.log(`Total refunded: $${totalRefunded / 100}`);
console.log(`Remaining refundable: $${remainingRefundable / 100}`);

Check Refund Status

// Customer: "Where's my refund?"
const response = await listReversals('TRtransfer789');
const reversals = response._embedded.transfers;

if (reversals.length === 0) {
console.log('No refund has been issued');
} else {
reversals.forEach(reversal => {
console.log(`Refund ID: ${reversal.id}`);
console.log(`Amount: $${reversal.amount / 100}`);
console.log(`Status: ${reversal.state}`);
console.log(`Created: ${reversal.created_at}`);

if (reversal.state === 'SUCCEEDED') {
console.log('Refund processed. Will appear in 5-10 business days.');
} else if (reversal.state === 'PENDING') {
console.log('Refund is processing. Please check back later.');
} else if (reversal.state === 'FAILED') {
console.log('Refund failed. Please contact support.');
}
});
}

Refund History Report

# Get all reversals for a transfer
curl -X GET \
'https://api.ahrvo.network/payments/na/transfers/TRtransfer789/reversals' \
-u username:password

# Display refund history
# Date | Amount | Reason | Status
# 2023-12-11 | $30 | partial_return | SUCCEEDED
# 2023-12-12 | $20 | partial_return | SUCCEEDED
# Total refunded: $50 of $100

Prevent Over-Refunding

// Before creating new refund, check existing
async function canRefund(transferId, requestedAmount) {
const transfer = await fetchTransfer(transferId);
const reversalsResponse = await listReversals(transferId);
const reversals = reversalsResponse._embedded.transfers;

// Calculate total already refunded (including pending)
const totalRefunded = reversals
.filter(r => r.state !== 'FAILED')
.reduce((sum, r) => sum + r.amount, 0);

// Check if new refund would exceed original
const totalAfterRefund = totalRefunded + requestedAmount;

if (totalAfterRefund > transfer.amount) {
throw new Error(
`Cannot refund $${requestedAmount / 100}. ` +
`Already refunded $${totalRefunded / 100} of $${transfer.amount / 100}.`
);
}

return true;
}

// Usage
await canRefund('TRtransfer789', 3000);
await createReversal('TRtransfer789', { refund_amount: 3000 });

Track Partial Refunds

// Display partial refund breakdown
const response = await listReversals('TRtransfer789');
const reversals = response._embedded.transfers;
const transfer = await fetchTransfer('TRtransfer789');

console.log(`Original Order: $${transfer.amount / 100}`);
console.log('Refund History:');

reversals.forEach((reversal, index) => {
const reason = reversal.tags?.reason || 'Unknown';
const items = reversal.tags?.items_returned || 'N/A';
console.log(
` ${index + 1}. $${reversal.amount / 100} - ${reason} ` +
`(${items} items) - ${reversal.state}`
);
});

const totalRefunded = reversals
.filter(r => r.state === 'SUCCEEDED')
.reduce((sum, r) => sum + r.amount, 0);

console.log(`Total Refunded: $${totalRefunded / 100}`);
console.log(`Net Sale: $${(transfer.amount - totalRefunded) / 100}`);

Best Practices

  • Check Before Creating: Always check existing reversals
    • Prevent over-refunding
    • Show customer refund history
    • Calculate remaining refundable amount
    • Display in admin UI
  • Include PENDING: When calculating totals
    • PENDING reversals count toward total
    • Will become SUCCEEDED soon
    • Prevents duplicate refunds
    • Only exclude FAILED
  • Store Reversal IDs: Track in your database
    • Link to orders
    • Customer service reference
    • Accounting reconciliation
    • Audit trail
  • Customer Communication: Use for status updates
    • List all refunds issued
    • Show amounts and dates
    • Display current status
    • Set expectations (5-10 days)
  • Error Handling: Handle empty lists
    • Check page.count or length
    • Display "No refunds" message
    • Don't assume refunds exist
    • Graceful UI
  • Tags: Analyze refund reasons
    • Track why refunds issued
    • reason tag patterns
    • Product quality issues
    • Customer satisfaction metrics
    • Improve processes

Common Workflows

Customer Refund Status Check

  1. Customer provides order number
  2. Look up order, get transfer ID
  3. List reversals for transfer
  4. If empty: "No refund issued"
  5. If exists: Show status and timeline
  6. If PENDING: "Processing, 5-10 days"
  7. If SUCCEEDED: "Processed, may take 5-10 days to appear"

Accounting Reconciliation

  1. Get all transfers for period
  2. For each transfer:
  3. List reversals
  4. Calculate net amount (original - refunds)
  5. Sum net amounts
  6. Compare to settlement totals
  7. Investigate discrepancies

Customer Service Dashboard

// Display order with refund info
async function getOrderSummary(orderId) {
// Get transfer from order
const transfer = await fetchTransferByOrder(orderId);

// Get all refunds
const reversalsResponse = await listReversals(transfer.id);
const reversals = reversalsResponse._embedded.transfers;

// Calculate totals
const totalRefunded = reversals
.filter(r => r.state === 'SUCCEEDED')
.reduce((sum, r) => sum + r.amount, 0);

const pendingRefunds = reversals
.filter(r => r.state === 'PENDING')
.reduce((sum, r) => sum + r.amount, 0);

return {
orderId,
chargeAmount: transfer.amount,
totalRefunded,
pendingRefunds,
netAmount: transfer.amount - totalRefunded - pendingRefunds,
refundHistory: reversals.map(r => ({
id: r.id,
amount: r.amount,
state: r.state,
date: r.created_at,
reason: r.tags?.reason
}))
};
}

Refund Analytics

// Analyze refund patterns
async function analyzeRefunds(merchantId, startDate, endDate) {
// Get all transfers in period
const transfers = await listTransfers({
merchant: merchantId,
'created_at.gte': startDate,
'created_at.lte': endDate,
state: 'SUCCEEDED'
});

let totalCharges = 0;
let totalRefunds = 0;
const refundReasons = {};

for (const transfer of transfers) {
totalCharges += transfer.amount;

// Get reversals for each transfer
const reversalsResponse = await listReversals(transfer.id);
const reversals = reversalsResponse._embedded.transfers;

reversals.forEach(reversal => {
if (reversal.state === 'SUCCEEDED') {
totalRefunds += reversal.amount;

const reason = reversal.tags?.reason || 'Unknown';
refundReasons[reason] = (refundReasons[reason] || 0) + 1;
}
});
}

return {
totalCharges: totalCharges / 100,
totalRefunds: totalRefunds / 100,
refundRate: (totalRefunds / totalCharges * 100).toFixed(2) + '%',
refundReasons
};
}
  • POST /transfers: Create original transfer
  • POST /transfers/{id}/reversals: Create new refund
  • GET /transfers: List all transfers (including reversals)
  • GET /transfers/{id}: Fetch transfer/reversal details
  • PUT /transfers/{id}: Update transfer/reversal tags