Retry a failed charge
Re-attempt a declined transaction against the same — or a different — payment method.
What you’ll build
A recovery step: a charge failed last night with insufficient_funds. Retry it now — on the same card, or on a new card the customer just added.
Which tool?
There are two ways to retry, and the right one depends on whether you’re keeping the same card:
| Use… | When | What it does |
|---|---|---|
retry_order | Same card on file — including a failed subscription renewal. | Re-attempts the charge on the same order. For a subscription cycle it reconciles the cycle, so the daily billing cron won’t charge again. |
retry_failed_charge | You need to charge a different card. | Reconstructs the order from the failed transaction and creates a new order on the chosen card. Does not reconcile the original subscription cycle. |
For a failed subscription renewal you want to move to a new card, update the subscription’s payment method first (
update_subscription), then callretry_order— that keeps future cycles on the new card and reconciles the cycle.
Same card — retry_order
{
"name": "retry_order",
"arguments": {
"order_id": "6ba7b811-9dad-11d1-80b4-00c04fd430c8",
"idempotency_key": "retry-2026-04-21-6ba7b811"
}
}
Only orders in failed or refund_failed status are eligible. The response is the updated order — check status (a re-decline leaves it failed with an incremented attempt_count). For a subscription cycle, success reconciles the cycle automatically.
Different card — retry_failed_charge
Use retry_failed_charge (a composite tool) to charge a different card.
{
"name": "retry_failed_charge",
"arguments": {
"transaction_id": "6ba7b813-9dad-11d1-80b4-00c04fd430c8",
"payment_method_id": "6ba7b815-9dad-11d1-80b4-00c04fd430c8",
"idempotency_key": "retry-2026-04-21-6ba7b813"
}
}
| Argument | Meaning |
|---|---|
transaction_id | The failed transaction to retry. |
payment_method_id | Optional. If omitted, retries against the same card; if present, charges the new card. |
idempotency_key | Pass a stable key per retry attempt so accidental re-runs do not double-charge. |
When to retry against the same card
- Decline reason was
insufficient_fundsand a few days have passed (paydays). - Decline reason was
gateway_error(transient).
When to retry against a different card
- Decline reason was
expired_cardand the customer added a new one. - Decline reason was
card_declinedrepeatedly — the issuer is consistently saying no.
What you get back
{
"transaction_id": "9c1d2e3f-4a5b-4c6d-8e7f-0123456789ab",
"status": "succeeded",
"amount": 2999,
"currency": "usd"
}
Or:
{
"error": {
"type": "processing_error",
"code": "card_declined",
"message": "Card was declined.",
"request_id": "req_a1b2c3d4e5f67890abcdef0123456789"
}
}
Stop retrying
Most issuers will eventually mark a card as “do not retry” if you keep hammering it. If you drive your own manual retry cadence on top of EPD’s daily cron, keep it finite — e.g. one manual retry after a likely-cleared decline, then stop and ask for a new card — and notify the customer at each step. (EPD’s cron itself just re-attempts the failed cycle once per daily run; it has no day-1/3/7 curve, so layering your own loop on top can over-charge attempts if you’re not careful.)
When a subscription renewal fails, EPD’s billing cron re-attempts the failed cycle on its next daily run (10:00 UTC) until it succeeds or the subscription ends — there is no accelerated backoff curve. Use retry_order to re-attempt a failed cycle on demand before then; it reconciles the cycle so the cron won’t also charge it. Reach for retry_failed_charge when you need to move the charge to a different card.