Skip to main content

Abandoned Cart Recovery Events

The following webhook events track the abandoned cart recovery lifecycle:
EventDescription
abandoned_checkout.detectedAn abandoned checkout has been detected. Sent when a payment is identified as abandoned (failed or incomplete) and the recovery workflow begins.
abandoned_checkout.recoveredThe customer completed payment through the recovery link. The recovered_payment_id field contains the successful payment ID.

Abandoned Checkout Payload Fields

payment_id
string
required
The original payment that was abandoned. Use this to look up product, amount, and currency details.
customer_id
string
required
The customer who abandoned the checkout.
abandonment_reason
string
required
Why the checkout was abandoned. One of:
  • payment_failed — Customer attempted payment but it failed
  • checkout_incomplete — Customer visited checkout but never attempted payment
status
string
required
Current lifecycle state of this recovery attempt. One of:
  • abandoned — Detected, no emails sent yet
  • recovering — At least one recovery email sent
  • recovered — Customer completed payment
  • exhausted — All emails sent or newer checkout found
  • opted_out — Customer unsubscribed
abandoned_at
string
required
ISO 8601 timestamp of when the checkout was detected as abandoned.
recovered_payment_id
string | null
The payment ID of the successful recovery payment. null until the checkout is recovered.

Example: Handling ACR Webhooks

app.post('/webhooks/dodo', async (req, res) => {
  const event = req.body;

  switch (event.type) {
    case 'abandoned_checkout.detected':
      console.log(`Checkout abandoned: ${event.data.payment_id}`);
      console.log(`Reason: ${event.data.abandonment_reason}`);
      // Track abandonment in your analytics
      await trackAbandonment(event.data);
      break;

    case 'abandoned_checkout.recovered':
      console.log(`Checkout recovered: ${event.data.payment_id}`);
      console.log(`Recovery payment: ${event.data.recovered_payment_id}`);
      // Grant access, update records
      await handleRecovery(event.data);
      break;
  }

  res.json({ received: true });
});

Dunning Events

The following webhook events track the subscription dunning lifecycle:
EventDescription
dunning.startedA dunning attempt has been created for a subscription that entered on_hold or was cancelled by the customer.
dunning.recoveredThe customer updated their payment method and the resulting payment succeeded. The payment_id field contains the successful payment ID.

Dunning Attempt Payload Fields

subscription_id
string
required
The subscription that triggered the dunning attempt.
customer_id
string
required
The customer who owns the subscription.
trigger_state
string
required
The subscription state that triggered dunning. One of:
  • on_hold — Subscription paused due to payment failure
  • cancelled — Customer cancelled from the customer portal
status
string
required
Current lifecycle state of this dunning attempt. One of:
  • recovering — Dunning emails are being sent
  • recovered — Customer updated payment method and payment succeeded
  • exhausted — All emails sent or subscription state changed
created_at
string
required
ISO 8601 timestamp of when the dunning attempt was created.
payment_id
string | null
The payment ID of the successful recovery payment. null while recovering.

Example: Handling Dunning Webhooks

app.post('/webhooks/dodo', async (req, res) => {
  const event = req.body;

  switch (event.type) {
    case 'dunning.started':
      console.log(`Dunning started for subscription: ${event.data.subscription_id}`);
      console.log(`Trigger: ${event.data.trigger_state}`);
      // Track dunning in your system
      await trackDunning(event.data);
      break;

    case 'dunning.recovered':
      console.log(`Subscription recovered: ${event.data.subscription_id}`);
      console.log(`Recovery payment: ${event.data.payment_id}`);
      // Reactivate access, update records
      await handleDunningRecovery(event.data);
      break;
  }

  res.json({ received: true });
});
Subscribe to both dunning.started and dunning.recovered to track the full dunning lifecycle. Use dunning.started to pause grace periods or flag at-risk subscriptions in your system.

Abandoned Cart Recovery

Configure ACR email sequences and discount incentives.

Subscription Dunning

Configure dunning email sequences for lapsed subscriptions.

Subscription Webhooks

Related subscription lifecycle events like subscription.on_hold and subscription.cancelled.

Webhook Payload Schema

Webhook payload for abandoned_checkout.detected and abandoned_checkout.recovered events

abandoned_at
string<date-time>
required
abandonment_reason
enum<string>
required
Available options:
payment_failed,
checkout_incomplete
customer_id
string
required
payment_id
string
required
status
enum<string>
required
Available options:
abandoned,
recovering,
recovered,
exhausted,
opted_out
recovered_payment_id
string | null
Last modified on April 8, 2026