API Status Support Dashboard

PRA Refund Decline Queue

Automatically manage and retry declined refunds (Payment Refund Authorization).


Overview

The Refund Decline Queue handles offline refund declines with automatic retry logic. When a refund is declined, it:


How It Works

1. Refund Attempted → 2. Declined → 3. Added to Queue
                                            ↓
4. Resolved ← 3. Auto-Retry (every 5min-24hr) ← Manual Retry

List Queue Entries

Endpoint

GET /api/v1/refund-decline-queue

Query Parameters

Parameter Type Description
status string Filter by status
merchantId string Filter by merchant
fromDate date Start date
toDate date End date
page integer Page number
limit integer Items per page
curl "https://dev.auxcore.net/api/v1/refund-decline-queue?status=pending" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: your-tenant-id"

Response:

{
  "success": true,
  "data": {
    "entries": [
      {
        "id": "rdq_abc123",
        "transactionId": "txn_original_456",
        "refundTransactionId": "ref_declined_789",
        "amount": 150.75,
        "currency": "USD",
        "status": "pending",
        "declineReason": "Account closed",
        "declineCode": "R02",
        "gatewayType": "NMI",
        "attemptCount": 1,
        "maxAttempts": 3,
        "lastAttemptAt": "2026-01-28T15:00:00Z",
        "scheduledRetryAt": "2026-01-28T15:05:00Z",
        "createdAt": "2026-01-28T15:00:00Z"
      }
    ],
    "pagination": {
      "page": 1,
      "limit": 50,
      "total": 15,
      "pages": 1
    }
  }
}

Queue Entry Statuses

Status Description
pending Waiting for next retry attempt
processing Currently being processed
retry In retry queue
resolved Successfully refunded
failed Max attempts reached, failed
cancelled Manually cancelled

Get Queue Entry

Endpoint

GET /api/v1/refund-decline-queue/:entryId
curl https://dev.auxcore.net/api/v1/refund-decline-queue/rdq_abc123 \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: your-tenant-id"

Response:

{
  "success": true,
  "data": {
    "id": "rdq_abc123",
    "transactionId": "txn_original_456",
    "refundTransactionId": "ref_declined_789",
    "amount": 150.75,
    "currency": "USD",
    "status": "retry",
    "declineReason": "Account closed",
    "declineCode": "R02",
    "gatewayType": "NMI",
    "gatewayResponse": {
      "code": "R02",
      "message": "Account closed"
    },
    "attemptCount": 2,
    "maxAttempts": 3,
    "lastAttemptAt": "2026-01-28T15:10:00Z",
    "scheduledRetryAt": "2026-01-28T15:20:00Z",
    "attempts": [
      {
        "attemptNumber": 1,
        "attemptedAt": "2026-01-28T15:00:00Z",
        "result": "declined",
        "error": "Account closed"
      },
      {
        "attemptNumber": 2,
        "attemptedAt": "2026-01-28T15:10:00Z",
        "result": "declined",
        "error": "Account closed"
      }
    ],
    "createdAt": "2026-01-28T15:00:00Z",
    "updatedAt": "2026-01-28T15:10:00Z"
  }
}

Manual Retry

Manually trigger a retry attempt.

Endpoint

POST /api/v1/refund-decline-queue/:entryId/retry
curl -X POST https://dev.auxcore.net/api/v1/refund-decline-queue/rdq_abc123/retry \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: your-tenant-id"

Response:

{
  "success": true,
  "data": {
    "id": "rdq_abc123",
    "status": "processing",
    "message": "Retry initiated",
    "scheduledRetryAt": "2026-01-28T15:15:00Z"
  }
}

⚠️ Note: Manual retries reset the attempt counter.


Manual Resolution

Mark an entry as resolved (e.g., refunded through different method).

Endpoint

POST /api/v1/refund-decline-queue/:entryId/resolve
curl -X POST https://dev.auxcore.net/api/v1/refund-decline-queue/rdq_abc123/resolve \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: your-tenant-id" \
  -H "Content-Type: application/json" \
  -d '{
    "notes": "Refunded via check",
    "refundMethod": "check",
    "refundDate": "2026-01-28"
  }'

Response:

{
  "success": true,
  "data": {
    "id": "rdq_abc123",
    "status": "resolved",
    "resolvedAt": "2026-01-28T15:30:00Z",
    "resolvedBy": "user_123",
    "resolutionNotes": "Refunded via check"
  }
}

Cancel Entry

Cancel a queue entry (e.g., customer no longer wants refund).

Endpoint

POST /api/v1/refund-decline-queue/:entryId/cancel
curl -X POST https://dev.auxcore.net/api/v1/refund-decline-queue/rdq_abc123/cancel \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: your-tenant-id" \
  -d '{
    "reason": "Customer request",
    "notes": "Customer called to cancel refund"
  }'

Response:

{
  "success": true,
  "data": {
    "id": "rdq_abc123",
    "status": "cancelled",
    "cancelledAt": "2026-01-28T15:40:00Z",
    "cancelledBy": "user_123",
    "cancellationReason": "Customer request"
  }
}

Automatic Retry Schedule

The system automatically retries declined refunds with exponential backoff:

Attempt Delay Cumulative Time
1 Immediate 0 min
2 5 minutes 5 min
3 10 minutes 15 min
4 20 minutes 35 min
5 1 hour 1h 35min
6 2 hours 3h 35min
7 6 hours 9h 35min
8 12 hours 21h 35min
9 24 hours 45h 35min

After max attempts (default 3), status becomes failed.


Process Retries (Cron Endpoint)

Trigger automatic retry processing (call this from a cron job).

Endpoint

POST /api/v1/refund-decline-queue/process-retries
curl -X POST https://dev.auxcore.net/api/v1/refund-decline-queue/process-retries \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: your-tenant-id"

Response:

{
  "success": true,
  "data": {
    "processed": 5,
    "resolved": 2,
    "requeued": 3,
    "failed": 0
  }
}

Cron Schedule

Recommended cron schedule (every 5 minutes):

*/5 * * * * curl -X POST https://dev.auxcore.net/api/v1/refund-decline-queue/process-retries \
  -H "X-Tenant-ID: your-tenant-id" \
  -H "x-internal-service-key: YOUR_KEY"

Common Decline Reasons

Code Reason Recommended Action
R02 Account closed Contact customer for new account
R03 No account found Verify account details
R04 Invalid account Check routing/account numbers
R10 Not authorized Customer needs to authorize
R29 Corporate action Contact customer's bank

Webhooks

refund_decline_queue.created

{
  "type": "refund_decline_queue.created",
  "data": {
    "id": "rdq_abc123",
    "transactionId": "txn_456",
    "amount": 150.75,
    "declineReason": "Account closed"
  }
}

refund_decline_queue.resolved

{
  "type": "refund_decline_queue.resolved",
  "data": {
    "id": "rdq_abc123",
    "transactionId": "txn_456",
    "amount": 150.75,
    "resolvedAt": "2026-01-28T16:00:00Z"
  }
}

refund_decline_queue.failed

{
  "type": "refund_decline_queue.failed",
  "data": {
    "id": "rdq_abc123",
    "transactionId": "txn_456",
    "amount": 150.75,
    "attemptCount": 3,
    "lastError": "Account closed"
  }
}

Analytics

Track queue performance:

curl "https://dev.auxcore.net/api/v1/refund-decline-queue/stats?period=30d" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: your-tenant-id"

Response:

{
  "success": true,
  "data": {
    "totalEntries": 150,
    "resolved": 120,
    "failed": 10,
    "pending": 20,
    "resolutionRate": 0.80,
    "averageAttempts": 2.3,
    "averageResolutionTime": "4h 30m"
  }
}

Best Practices

✅ DO:

❌ DON'T:


Troubleshooting

High Failure Rate

If seeing many failures:

  1. Check decline reasons
  2. Contact gateway support
  3. Review customer account statuses
  4. Consider alternative refund methods

Queue Not Processing

If retries aren't happening:

  1. Verify cron is running
  2. Check server logs
  3. Test /process-retries endpoint
  4. Ensure service is healthy

Stuck Entries

If entries stuck in processing:

  1. Check for service errors
  2. Manually retry
  3. Contact support if persists

Next Steps


Need help? Contact support@auxvault.com