Back to blog

GuideOctober 26, 20267 min read

How to handle failed payments in Dodo Payments

Detect failed Dodo Payments charges with webhooks, understand what Dodo's retries and dunning do on their own, and run a short recovery workflow on top, with read-only access.

To handle failed payments in Dodo Payments, listen for the payment.failed and subscription.on_hold webhooks, verify each signature, and decide for each customer whether to wait for Dodo's own recovery or reach out. Dodo can retry soft declines on renewals and email customers through dunning once you enable them. MRRdue adds a ranked queue, a drafted email per case and alerts, using read-only access.

Neither layer is complete on its own. Dodo's recovery features are off until you configure them and cover only some failures. A queue that ignores what Dodo is already doing ends up emailing customers twice. Here is how the two fit together.

How Dodo signals a failed payment

Dodo's documentation calls the payment.failed webhook the most reliable way to detect a failure. When a subscription renewal fails, the subscription moves to on_hold and a subscription.on_hold event fires. If your business has a grace period, subscription.past_due fires first, and the customer keeps access until the grace period ends.

Check the payment's status before you read error_code or error_message, because both are empty until a payment actually fails. A payment linked to a subscription is a renewal. A payment with no subscription is a one-time charge, and Dodo never retries those automatically.

Verify and deduplicate Dodo webhooks

Dodo webhooks follow the Standard Webhooks specification. Each delivery carries webhook-id, webhook-timestamp and webhook-signature headers, and the signature is an HMAC-SHA256 over the ID, the timestamp and the raw body, made with your endpoint's secret. Reject deliveries whose timestamp is too far from the current time; the Standard Webhooks libraries allow five minutes.

Store each webhook-id and skip any you have seen, because a retry can deliver the same event more than once, and events can arrive out of order. Dodo's docs describe up to eight delivery attempts with exponential backoff, spread over roughly a day, and a 30-second timeout per attempt. The Dodo dashboard logs every attempt. Dodo's docs also note that the dashboard does not send email alerts for failing deliveries, so you need to watch for that yourself.

What Dodo Payments does on its own

Subscription Payment Retries automatically retries soft declines, such as insufficient funds or network errors, on scheduled renewal payments. It is off by default. It does not retry hard declines, first payments, one-time payments, plan-change charges or on-demand charges.

Subscription Dunning emails customers whose subscription is on hold, past due or cancelled, with a link to the Customer Portal to update their payment method. It is also off by default and is turned on under Settings, then Recovery. Dodo also offers a Manual Payment Retry from the dashboard or the API, and an Update Payment Method API that charges any outstanding dues. These descriptions follow Dodo's documentation at the time of writing; confirm the current behaviour in the official docs.

What MRRdue adds on top

You connect MRRdue with a Dodo Payments API key. At connection time MRRdue checks whether the key can write to your account. If it can, MRRdue warns you and stores the key only after you explicitly acknowledge that. Its Dodo client then only reads data; the one write-shaped call is the connection probe against a customer ID that cannot exist. The key is encrypted at rest.

You point a Dodo webhook at your workspace's own MRRdue monitoring URL and store its signing secret. Each verified event makes MRRdue re-fetch the referenced subscription or payment from Dodo, and a scheduled sync catches anything a webhook missed. Every failed payment becomes a case with a normalised failure reason, a risk score and a suggested action, ranked against everything else that is open. You get an alert by email or Slack. A case is marked recovered automatically when the same subscription later has a successful payment.

Each case comes with a drafted recovery email. Dodo payments do not carry a hosted invoice link MRRdue can attach, so the draft points the customer to the billing link in your product or invites them to reply. You can add a Customer Portal link yourself. Webhook Doctor also watches the Dodo webhook for signature rejections and for events received but never processed.

A step-by-step workflow

1. In Dodo, decide what Dodo should handle itself: turn on Subscription Payment Retries for soft declines, and turn on Subscription Dunning if you want Dodo's emails.

2. Connect MRRdue with a Dodo API key; a read-only key is recommended. Add the MRRdue monitoring URL as a webhook endpoint in Dodo and store its signing secret in MRRdue.

3. Let the first sync import your history, then work the queue from the top, ranked by revenue at risk.

4. For each case, check what Dodo is already doing. A renewal soft decline with retries switched on can often wait a few days. A hard decline, an on-hold subscription or a one-time charge needs a person: send the drafted email with a clear way to update the payment method.

5. If Dodo's dunning is on, don't duplicate it. Use MRRdue's queue for the high-value accounts that deserve a personal note, not for a second automated sequence.

6. Let recovered cases close themselves when a later payment succeeds, and mark the rest lost or ignored so the queue stays honest. Glance at Webhook Doctor's status, so you know when the data feeding the queue has stopped arriving.

Limitations

MRRdue does not retry payments, create payment-update or checkout links, change a subscription's status, or reactivate an on-hold subscription. Do all of that in Dodo. MRRdue never moves money or changes billing settings in the connected account.

Real-time updates need the webhook signing secret stored in MRRdue. Without it, changes arrive on the scheduled sync instead. Webhook Doctor's comparison against the provider's own event list is Stripe-only, so on Dodo connections it relies on signature and processing signals. Drafts for Dodo cases have no hosted invoice link built in.

Frequently asked questions

Does Dodo Payments retry failed payments automatically? Only soft declines on scheduled subscription renewals, and only if you turn on Subscription Payment Retries, which Dodo's docs list as off by default. One-time payments are not retried automatically.

Which Dodo webhook events should I listen to? payment.failed for every failed charge, plus subscription.on_hold for failed renewals, and subscription.past_due if you use a grace period.

Can MRRdue reactivate an on-hold subscription? No. MRRdue has read-only access. The customer updates their payment method through Dodo, for example in the Customer Portal or through the Update Payment Method API, and Dodo reactivates the subscription once the outstanding payment succeeds.

Does MRRdue email my customers automatically? Not by default. By default, MRRdue drafts each recovery email and a person reviews and sends it: by hand, through your own systems (Slack, a signed webhook, n8n, Zapier or Make), or with MRRdue's managed sender once you switch it on. Automatic sending is an optional, off-by-default setting on the paid plan (not available during the trial). It requires managed sending, and it only emails cases at or above a risk threshold you choose (low, medium or high; high by default).

Is this the same Dodo account MRRdue bills me through? No. MRRdue's own subscription is billed through Dodo Payments, but that is completely separate from the account you connect for monitoring.

Related reading

See your own recoverable revenue

Explore the demo dashboard or estimate recoverable dollars with the free calculator. No billing connection required to start.