Developer dashboard

Dunning Management#

How AuxCORE handles failed recurring payments: automatic retries, customer notices, and recovery tools.


Overview#

When a scheduled charge is declined, AuxCORE automatically:

  • 🔄 Auto-retries - Retries the charge on a fixed schedule
  • 📧 Notifies the customer - Payment-failed email after each failed run, a dunning notice when retries run out
  • ⏸️ Auto-pauses - Subscriptions are paused after the last retry; payment plans become defaulted
  • 📊 Tracks attempts - Failure counters and an activity log on every schedule

All endpoints on this page use admin authentication (Authorization: Bearer YOUR_JWT + X-Tenant-ID). See Subscriptions.


How Dunning Works#

Subscriptions (recurring schedules)#

text
Payment fails → retry after 24h → retry after 24h → ... → max attempts reached → Pause + dunning email
  • Each schedule allows maxRetryAttempts retries (default 3).
  • After each decline, failCount and failedPaymentCount increase, lastFailedPaymentDate is set and the next retry is scheduled 24 hours later (nextRetryAt).
  • Due charges are processed every hour; failed charges are also swept every 6 hours.
  • When the retries are exhausted the schedule becomes paused (isRecurring: false, pausedReason: "Payment failed after 3 retry attempts") and a dunning email is sent once (only when the schedule has sendNotifications: true and the merchant's failedPaymentNoticeEnabled is on).
  • A successful charge resets failedPaymentCount, retryCount and nextRetryAt.

Payment Plans#

  • A declined installment is retried every retryIntervalDays (default 3 days) up to maxRetryAttempts (default 3) attempts, while retryFailedPayments is true.
  • While a retry is pending the installment status is pending with nextRetryAt set.
  • After the last failed attempt the plan becomes defaulted.
  • Late fees (if configured) keep accruing on overdue installments and are added to the retried charge. See Payment Plans.

Note: The retry schedule and the action after the last retry are fixed by the platform. There are no dunning-settings endpoints, no configurable retry schedule, no grace period and no cancel / manual_review action.


Failure Notice Setting#

The only merchant-level dunning control is whether the failed-payment notice is sent. It is part of the merchant's messaging defaults.

Get Messaging Defaults#

GET/api/v1/merchants/:merchantId/messaging-defaults
Get Messaging Defaultsbash
curl https://uat.auxcore.net/api/v1/merchants/c1a7e5d2-3b4f-4e6a-9c8d-7f0b1a2e3d45/messaging-defaults \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: your-tenant-id"

Response:

Get Messaging Defaultsjson
{
  "success": true,
  "data": {
    "successfulPaymentReceiptEnabled": true,
    "recurringPaymentReminderEnabled": true,
    "failedPaymentNoticeEnabled": true,
    "usingDefaults": true
  }
}

Update Messaging Defaults#

PUT/api/v1/merchants/:merchantId/messaging-defaults
Update Messaging Defaultsbash
curl -X PUT https://uat.auxcore.net/api/v1/merchants/c1a7e5d2-3b4f-4e6a-9c8d-7f0b1a2e3d45/messaging-defaults \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: your-tenant-id" \
  -H "Content-Type: application/json" \
  -d '{ "failedPaymentNoticeEnabled": false }'

All three flags are optional booleans; omitted flags keep their value. Admin roles can read and change any merchant; merchant users only their own merchant. Any other caller gets 403 ({ "success": false, "message": "..." }).

Setting failedPaymentNoticeEnabled to false suppresses the dunning email. Per-schedule notifications can also be off (sendNotifications: false on the schedule).


Monitor Failed Payments#

There is no dedicated failed-payments endpoint. Use these instead:

  • GET /api/v1/recurring/overview — data.failed is the number of schedules with at least one failed payment.
  • GET /api/v1/recurring/:id — failedPaymentCount, retryCount, maxRetryAttempts, nextRetryAt, lastFailedPaymentDate, pausedReason.
  • GET /api/v1/recurring/:id/schedule — cycle rows with status failed.
  • GET /api/v1/billing/payment-plans?status=defaulted — defaulted payment plans.

Retry Failed Payment#

Reset the retry counters of a subscription so the scheduler retries on its next run. This does not charge immediately; use Charge Now for that. Returns 400 "No failed payments to retry" when failedPaymentCount is 0.

Endpoint#

POST/api/v1/recurring/:id/retry-failed
Retry Failed Paymentbash
curl -X POST https://uat.auxcore.net/api/v1/recurring/8d2f1c4b-6a3e-4f7d-b1c9-0e5a7d3f2b61/retry-failed \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: your-tenant-id"

Response:

Retry Failed Paymentjson
{
  "success": true,
  "data": {
    "id": "8d2f1c4b-6a3e-4f7d-b1c9-0e5a7d3f2b61",
    "status": "active",
    "failedPaymentCount": 0,
    "retryCount": 0,
    "nextRetryAt": "2026-10-01T16:45:00.000Z"
  },
  "message": "Payment retry initiated successfully"
}

Note: A schedule that was auto-paused must also be resumed (POST /api/v1/recurring/:id/resume) before the scheduler charges it again.


Charge Now#

Charge the next payment immediately.

Endpoint#

POST/api/v1/recurring/:id/charge-now

Response:

Charge Nowjson
{
  "success": true,
  "data": { "success": true, "transactionId": "5b9e2d7a-1c3f-4a8e-b6d0-2f4c8a1e7b93" },
  "message": "Payment processed successfully"
}

A decline returns 400 with error.code PROCESSING_ERROR. A second attempt within 60 seconds is rejected. For payment plans use POST /api/v1/billing/payment-plans/:id/charge-now (decline → 402).


Update the Customer's Card#

Most failures are fixed by a new card.

Emails the customer a secure link (valid 24 hours) to replace the card on file.

POST/api/v1/recurring/:id/send-method-update-link

For payment plans: POST /api/v1/billing/payment-plans/:id/send-method-update-link.

Attach a card you collected yourself (PCI vault token). It becomes the default method; the card is not charged.

POST/api/v1/recurring/:id/link-payment-method
Link a New Cardjson
{ "pciToken": "pci_tok_1a2b3c4d5e6f", "last4": "4242", "brand": "visa" }

For payment plans: POST /api/v1/billing/payment-plans/:id/link-payment-method.


Notifications#

AuxCORE does not emit dunning webhook events. Each attempt is an ordinary transaction, so declined attempts appear as declined transactions. Customer emails:

Email When
Payment failed After each failed automatic charge of a subscription
Dunning notice Once, when a subscription is paused after its last retry (unless failedPaymentNoticeEnabled is false)
Payment successful After a successful charge, including a recovered one

Common Failure Reasons#

Reason Common Cause Customer Action
Insufficient funds Not enough balance Add funds to account
Card declined Card issuer declined Try different card
Card expired Card past expiry date Update card details
Invalid card Card info incorrect Verify card details
Stop recurring (codes 261/262) Cardholder asked the issuer to stop Contact the customer; do not retry

Best Practices#

✅ DO:#

  • Watch the overview - Follow up on schedules with failed payments
  • Send the update link early - Before the last retry runs
  • Resume after fixing - Auto-paused schedules stay paused until resumed
  • Keep failure notices on - Customers can only fix what they know about

❌ DON'T:#

  • Don't hammer Charge Now - Repeated declines can trigger issuer blocks
  • Don't retry stop-recurring declines - The cardholder revoked the authorization
  • Don't cancel immediately - A paused schedule can be resumed

Testing Dunning#

bash
# 1. Create a schedule with a card that will decline in the Sandbox, due today
# 2. Charge it now and confirm the decline
curl -X POST https://uat.auxcore.net/api/v1/recurring/8d2f1c4b-6a3e-4f7d-b1c9-0e5a7d3f2b61/charge-now \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: your-tenant-id"

# 3. Inspect the retry state
curl https://uat.auxcore.net/api/v1/recurring/8d2f1c4b-6a3e-4f7d-b1c9-0e5a7d3f2b61 \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: your-tenant-id"

See Testing for Sandbox decline cards.


Next Steps#


Need help? Contact support@auxcore.net