> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Manual Payment Retry

> Retry a failed subscription renewal payment on demand from the dashboard or the API instead of waiting for the next automatic retry.

<Info>
  Manual Retry re-attempts a failed subscription **renewal** payment the moment you ask, from the payment's detail page or through the API. It charges the payment method saved on the subscription, and it runs independently of the automatic [Payment Retries](/features/recovery/payment-retries) schedule.
</Info>

## What Is Manual Retry?

When a renewal payment fails, the subscription moves to `on_hold` and [Payment Retries](/features/recovery/payment-retries) re-attempt the charge on a back-off schedule. Sometimes you know the payment will go through now: a customer has confirmed they topped up their account, or your support team is on a call with them. Manual Retry lets you send one attempt immediately instead of waiting hours or days for the next scheduled one.

* **Renewal payments only**: Manual Retry applies to subscription renewal invoices while the subscription is `on_hold`. First payments, one-time payments, plan-change charges, and on-demand charges are not eligible.
* **No customer action**: The charge goes to the payment method already saved on the subscription.
* **Independent of automatic retries**: A manual retry does not consume an attempt from the automatic schedule, does not move the next scheduled retry, and works even when Payment Retries are turned off.
* **Retries the invoice, not the payment**: A failed payment is only the entry point. Dodo Payments looks up the open renewal invoice behind it and charges that debt, so it does not matter which failed payment on the invoice you retry from.

## Retrying from the Dashboard

<Steps>
  <Step title="Open the failed payment">
    Go to **Transactions → Payments** and click the failed renewal payment to open its **Transaction details** page.
  </Step>

  <Step title="Click Retry Payment Manually">
    Click **Retry Payment Manually** in the top-right corner. The button is available only while the payment is [eligible](#eligibility).
  </Step>

  <Step title="Check the result">
    A new payment is created for the attempt and appears in the **Activity Log**. If the charge succeeds, the subscription returns to `active` and the next billing date advances as normal. If the payment processor has not settled the charge yet, the payment shows as in progress until the `payment.succeeded` or `payment.failed` webhook reports the outcome.
  </Step>
</Steps>

<Frame caption="Retry Payment Manually on the transaction details page of a failed renewal">
  <img src="https://mintcdn.com/dodopayments/0duTS18kYi2NwQ3m/images/recovery/manual-retry-transaction-details.png?fit=max&auto=format&n=0duTS18kYi2NwQ3m&q=85&s=5537fa5eff17cbe91a53599f887a26e7" alt="Transaction details page for a failed payment showing the error code and message, an Activity Log, and a Retry Payment Manually button" style={{ maxHeight: '500px', width: 'auto' }} width="1285" height="698" data-path="images/recovery/manual-retry-transaction-details.png" />
</Frame>

## Eligibility

A manual retry is sent only when every check below passes. The **Reason code** column is what the API returns: in `reason` on `GET /payments/{payment_id}/retry`, and as the error `code` on `POST /payments/{payment_id}/retry`.

| Check                  | Requirement                                                                                                                                                                                                                                           | Reason code                                     |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| Payment type           | A subscription **renewal** payment whose invoice is still open. Payments with no invoice, first payments, one-time payments, plan-change charges, and on-demand charges cannot be retried.                                                            | `PAYMENT_NOT_RETRYABLE`                         |
| Subscription status    | `on_hold`                                                                                                                                                                                                                                             | `SUBSCRIPTION_INACTIVE`                         |
| Scheduled cancellation | The subscription is not scheduled to cancel at the next billing date.                                                                                                                                                                                 | `CHARGE_NOT_ALLOWED_FOR_SCHEDULED_CANCELLATION` |
| Saved payment method   | The subscription has a saved payment method to charge.                                                                                                                                                                                                | `SUBSCRIPTION_HAS_NO_PAYMENT_METHOD`            |
| Last failure           | The most recent failure is a **soft decline**. A hard decline, or a failure with no classified error code, cannot be retried.                                                                                                                         | `MANUAL_RETRY_HARD_DECLINE`                     |
| Nothing in flight      | No payment on the invoice is `processing` or has no recorded status yet. This is an attempt, manual or automatic, that was just sent and has not reported back. Wait for its outcome first.                                                           | `MANUAL_RETRY_IN_FLIGHT`                        |
| Newest payment failed  | The most recent payment on the invoice has `failed` status. A newest payment in any other state that is not `failed`, such as `requires_customer_action`, `requires_payment_method`, or `cancelled`, blocks the retry even when nothing is in flight. | `PREVIOUS_PAYMENT_PENDING`                      |
| Not already paid       | No payment on the invoice has succeeded.                                                                                                                                                                                                              | `MANUAL_RETRY_ALREADY_PAID`                     |
| Retry limit            | Fewer than 3 manual retries sent on the invoice, and the cooldown has passed. See [Retry Limits](#retry-limits).                                                                                                                                      | `MANUAL_RETRY_LIMIT_REACHED`                    |
| Customer               | The customer is not on your [blocklist](/features/customer-blocklist).                                                                                                                                                                                | `PAYMENT_NOT_RETRYABLE`                         |
| Payment connector      | For [BYOP](/features/byop) subscriptions, the connector is enabled.                                                                                                                                                                                   | `BYOP_CONNECTOR_DISABLED`                       |
| Live mode              | In live mode, your business has live payments enabled.                                                                                                                                                                                                | `MERCHANT_NOT_LIVE`                             |

<Note>
  Manual Retry is narrower than automatic retries in one place: it requires the subscription to be `on_hold`. Automatic retries keep running for other non-active statuses; see [Subscription Status Transitions](/features/recovery/payment-retries#subscription-status-transitions).
</Note>

<Warning>
  Retrying a hard decline against the same card cannot succeed, and repeated declines hurt your authorization rate. When the reason is `MANUAL_RETRY_HARD_DECLINE`, ask the customer to update their payment method instead. [Subscription Dunning](/features/recovery/subscription-dunning) does this automatically.
</Warning>

## Retry Limits

Each renewal invoice allows **3** manual retries, with a cooldown between them:

| Manual retry | Available                          |
| ------------ | ---------------------------------- |
| 1            | As soon as the payment is eligible |
| 2            | 1 hour after the first             |
| 3            | 3 hours after the second           |

The limits apply in both test mode and live mode. When a retry is refused for this reason, the API returns `MANUAL_RETRY_LIMIT_REACHED` (HTTP `429`). The error body carries only `code` and `message`. To learn when the next retry opens, [check the retry state](#check-whether-a-payment-can-be-retried) and read `retry_available_at`. It is `null` once all three are spent.

Automatic retries do not count toward this limit, and manual retries do not count toward the automatic schedule's 8 attempts.

## Manual vs. Automatic Retries

|                                   | Manual Retry                                                           | Payment Retries                                                 |
| --------------------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------------- |
| **Trigger**                       | You, from the dashboard or the API                                     | Dodo Payments, on a back-off schedule                           |
| **Timing**                        | Immediately                                                            | 12 hours after the failure, then progressively later            |
| **Attempts**                      | 3 per invoice, with a 1 hour and then a 3 hour cooldown                | Up to 8 per invoice, inside your recovery window                |
| **Needs Payment Retries enabled** | No                                                                     | Yes                                                             |
| **Effect on the other**           | None. A manual failure does not schedule or move an automatic attempt. | None. The automatic chain continues regardless of manual sends. |
| **Analytics**                     | Counted in the **Payment retries** metrics on the Recovery tab         | Counted in the same metrics                                     |

## Retrying via the API

Check eligibility first, then send the retry. Both endpoints take the ID of a failed payment.

### Check Whether a Payment Can Be Retried

`GET /payments/{payment_id}/retry` never fails on an ineligible payment. It returns `can_retry: false` with the `reason` code instead, so your dashboard or support tooling can show the same state the Dodo Payments dashboard shows. It requires the **Viewer** role.

<CodeGroup>
  ```typescript Node.js theme={null}
  import DodoPayments from 'dodopayments';

  const client = new DodoPayments({
    bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  });

  const state = await client.payments.retrieveRetryState('pay_0NmDtkE0iRvmeTcT6t0ol');

  if (state.can_retry) {
    console.log(`Retry available. ${state.sends_used}/${state.sends_allowed} used.`);
  } else {
    console.log(`Cannot retry: ${state.reason}. Next window: ${state.retry_available_at}`);
  }
  ```

  ```python Python theme={null}
  import os
  from dodopayments import DodoPayments

  client = DodoPayments(bearer_token=os.environ["DODO_PAYMENTS_API_KEY"])

  state = client.payments.retrieve_retry_state("pay_0NmDtkE0iRvmeTcT6t0ol")

  if state.can_retry:
      print(f"Retry available. {state.sends_used}/{state.sends_allowed} used.")
  else:
      print(f"Cannot retry: {state.reason}. Next window: {state.retry_available_at}")
  ```

  ```bash cURL theme={null}
  curl https://live.dodopayments.com/payments/pay_0NmDtkE0iRvmeTcT6t0ol/retry \
    -H "Authorization: Bearer $DODO_PAYMENTS_API_KEY"
  ```
</CodeGroup>

```json Response theme={null}
{
  "can_retry": false,
  "reason": "MANUAL_RETRY_LIMIT_REACHED",
  "sends_used": 1,
  "sends_allowed": 3,
  "retry_available_at": "2026-08-26T16:51:00Z"
}
```

| Field                | Description                                                                                                              |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `can_retry`          | `true` when a retry would be sent right now.                                                                             |
| `reason`             | The code the retry would fail with. `null` when `can_retry` is `true`.                                                   |
| `sends_used`         | Manual retries already sent on this invoice.                                                                             |
| `sends_allowed`      | Always `3`.                                                                                                              |
| `retry_available_at` | When the next manual retry opens. `null` when no retry is left, or when the refusal has nothing to do with the cooldown. |

### Send a Manual Retry

`POST /payments/{payment_id}/retry` creates a new payment and charges the saved payment method. It requires the **Editor** role.

<CodeGroup>
  ```typescript Node.js theme={null}
  const retry = await client.payments.retry('pay_0NmDtkE0iRvmeTcT6t0ol');

  console.log(retry.payment_id, retry.status);
  ```

  ```python Python theme={null}
  retry = client.payments.retry("pay_0NmDtkE0iRvmeTcT6t0ol")

  print(retry.payment_id, retry.status)
  ```

  ```bash cURL theme={null}
  curl -X POST https://live.dodopayments.com/payments/pay_0NmDtkE0iRvmeTcT6t0ol/retry \
    -H "Authorization: Bearer $DODO_PAYMENTS_API_KEY"
  ```
</CodeGroup>

```json Response theme={null}
{
  "payment_id": "pay_2IjeQm4hqU6RA4Z4kwDee",
  "invoice_id": "inv_9Kp2mQ7vRt4LxYw3",
  "status": "processing",
  "retry_attempt": 1,
  "is_manual_retry": true,
  "sends_used": 1,
  "sends_allowed": 3,
  "retry_available_at": "2026-08-26T16:51:00Z"
}
```

| Field                                               | Description                                                                                                                                                                                                        |
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `payment_id`                                        | The new payment created for this attempt.                                                                                                                                                                          |
| `invoice_id`                                        | The renewal invoice that was charged.                                                                                                                                                                              |
| `status`                                            | Outcome of the charge. `processing` means the processor has not settled it yet. `null` means no outcome was recorded before the response was returned. In both cases the payment webhooks report the final result. |
| `retry_attempt`                                     | Position of this attempt among the manual retries on the invoice, starting at `1`.                                                                                                                                 |
| `is_manual_retry`                                   | Always `true` on this endpoint.                                                                                                                                                                                    |
| `sends_used`, `sends_allowed`, `retry_available_at` | Retry limit state after this send. `retry_available_at` is the cooldown clock only. It is set even when this charge succeeds, in which case the invoice is paid and no further retry opens.                        |

### Error Responses

| HTTP status | Codes                                                                                                                                                                                            | What to do                                                                                                                                             |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `404`       | `NOT_FOUND`                                                                                                                                                                                      | The payment does not belong to your business.                                                                                                          |
| `409`       | `MANUAL_RETRY_IN_FLIGHT`, `PREVIOUS_PAYMENT_PENDING`, `CHARGE_NOT_ALLOWED_FOR_SCHEDULED_CANCELLATION`                                                                                            | Temporary, or something else must change first. Wait for the in-flight or pending payment to reach a final state, or clear the scheduled cancellation. |
| `422`       | `PAYMENT_NOT_RETRYABLE`, `SUBSCRIPTION_INACTIVE`, `SUBSCRIPTION_HAS_NO_PAYMENT_METHOD`, `MANUAL_RETRY_HARD_DECLINE`, `MANUAL_RETRY_ALREADY_PAID`, `BYOP_CONNECTOR_DISABLED`, `MERCHANT_NOT_LIVE` | This payment cannot be retried. Do not repeat the call.                                                                                                |
| `429`       | `MANUAL_RETRY_LIMIT_REACHED`                                                                                                                                                                     | [Check the retry state](#check-whether-a-payment-can-be-retried) and wait until its `retry_available_at`, or stop once all three retries are spent.    |

Every code is described in the [Error Codes](/api-reference/error-codes) reference.

## Webhooks

A manual retry creates an ordinary payment, so the same webhooks fire as for any renewal attempt:

| Event                | Fires when                                                                                                           |
| -------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `payment.succeeded`  | The retry was charged. `subscription.active` follows as the subscription is reactivated.                             |
| `payment.failed`     | The retry was declined. The subscription stays `on_hold`, and no automatic retry is scheduled from a manual failure. |
| `payment.processing` | The processor has accepted the charge but not settled it yet.                                                        |

On the payment object in these events, `retry_attempt` is `1` or higher and `subscription_id` is set, exactly as for an automatic retry. Keep the `payment_id` from the retry response if you need to tell a manual attempt apart from a scheduled one.

<Card title="Payment Webhook Payloads" icon="webhook" href="/developer-resources/webhooks/intents/payment">
  Full payload schemas for payment events.
</Card>

## Related

<CardGroup cols={2}>
  <Card title="Subscription Payment Retries" icon="arrow-rotate-right" href="/features/recovery/payment-retries">
    The automatic back-off schedule that runs alongside manual retries.
  </Card>

  <Card title="Subscription Dunning" icon="repeat" href="/features/recovery/subscription-dunning">
    Email the customer to update their payment method after a hard decline.
  </Card>

  <Card title="Handle Payment Failures" icon="screwdriver-wrench" href="/developer-resources/handle-payment-failures">
    Read decline codes and decide when a retry is worthwhile.
  </Card>

  <Card title="Error Codes" icon="triangle-exclamation" href="/api-reference/error-codes">
    Every `MANUAL_RETRY_*` code, its trigger, and its message.
  </Card>
</CardGroup>
