Skip to main content

Prerequisites

To integrate the Dodo Payments API, you’ll need:
  • A Dodo Payments merchant account
  • API credentials (API key and webhook secret key) from the dashboard
For a more detailed guide on the prerequisites, check this section.

API Integration

Checkout Sessions

Use Checkout Sessions to sell subscription products with a secure, hosted checkout. Pass your subscription product in product_cart and redirect customers to the returned checkout_url.
Mixed Checkout: You can combine subscription products with one-time products in the same checkout session. This enables use cases like setup fees with subscriptions, hardware bundles with SaaS, and more. See the Checkout Sessions guide for examples.

API Response

The following is an example of the response:
Redirect the customer to checkout_url.

Webhooks

When integrating subscriptions, you’ll receive webhooks to track the subscription lifecycle. These webhooks help you manage subscription states and payment scenarios effectively. To set up your webhook endpoint, please follow our Detailed Integration Guide.

Subscription Event Types

The following webhook events track subscription status changes:
  1. subscription.active - Subscription is successfully activated.
  2. subscription.updated - Subscription object was updated (fires on any field change).
  3. subscription.on_hold - Subscription is put on hold due to failed renewal.
  4. subscription.failed - Subscription creation failed during mandate creation.
  5. subscription.renewed - Subscription is renewed for the next billing period.
For reliable subscription lifecycle management, we recommend tracking these subscription events.
Use subscription.updated to get real-time notifications about any subscription changes, keeping your application state in sync without polling the API.

Payment Scenarios

Successful Payment Flow The webhooks you receive, and their timing, depend on whether the product has a trial. Immediate billing (0 trial days):
  1. subscription.active: the mandate is authorized and the subscription is activated.
  2. payment.succeeded: confirms the first charge. Expect this within 2–10 minutes of checkout.
With a trial period:
  1. At trial start (checkout): subscription.active fires once the payment method is authorized. No recurring charge is taken yet. The first real charge is deferred until the trial ends.
  2. At trial end: the recurring amount is charged, and you receive payment.succeeded together with subscription.renewed.
Every subsequent renewal:
  • subscription.renewed: fires on each billing cycle when the renewal payment is deducted, always alongside payment.succeeded. It also carries the updated next_billing_date.
Whenever money is actually deducted for a subscription product, you get subscription.renewed and payment.succeeded. Use subscription.renewed (rather than payment.succeeded alone) as your signal to extend access for the next cycle.
Payment Failure Scenarios
  1. Subscription Failure
  • subscription.failed - Subscription creation failed due to failure to create a mandate.
  • payment.failed - Indicates failed payment.
  1. Subscription On Hold
  • subscription.on_hold - Subscription is put on hold due to failed renewal payment or failed plan change charge.
  • When a subscription goes on hold, it will not renew automatically until the payment method is updated.
Best Practice: To simplify implementation, we recommend primarily tracking subscription events for managing the subscription lifecycle.
For a complete walkthrough of reading error_code/error_message, deciding when to retry, and surfacing failures to customers, see Handle Payment Failures.

subscription.failed vs. subscription.on_hold

These two events are easy to confuse, but they require very different handling:
subscription.failed is terminal. The subscription cannot be reactivated. The customer must create a new subscription. Never grant entitlements when this event fires.

Handling Subscription On Hold

When a subscription enters on_hold state, you need to update the payment method to reactivate it. This section explains when subscriptions go on hold and how to handle them.

When Subscriptions Go On Hold

A subscription is placed on hold when:
  • Renewal payment fails: The automatic renewal charge fails due to insufficient funds, expired card, or bank decline
  • Plan change charge fails: An immediate charge during plan upgrade/downgrade fails
  • Payment method authorization fails: The payment method cannot be authorized for recurring charges
Subscriptions in on_hold state will not renew automatically. You must update the payment method to reactivate the subscription.

Reactivating Subscriptions from On Hold

To reactivate a subscription from on_hold state, use the Update Payment Method API. This automatically:
  1. Creates a charge for remaining dues
  2. Generates an invoice for the charge
  3. Processes the payment using the new payment method
  4. Reactivates the subscription to active state upon successful payment
1

Handle subscription.on_hold webhook

When you receive a subscription.on_hold webhook, update your application state and notify the customer:
2

Update payment method

When the customer is ready to update their payment method, call the Update Payment Method API:
You can also use an existing payment method ID if the customer has saved payment methods:
3

Monitor webhook events

After updating the payment method, monitor for these webhook events:
  1. payment.succeeded - The charge for remaining dues was successful
  2. subscription.active - The subscription has been reactivated

Sample Subscription event payload


Changing Subscription Plans

You can upgrade or downgrade a subscription plan using the change plan API endpoint. This allows you to modify the subscription’s product, quantity, and handle proration.

Change Plan API Reference

For detailed information about changing subscription plans, please refer to our Change Plan API documentation.

Proration Options

When changing subscription plans, you have two options for handling the immediate charge:

1. prorated_immediately

  • Calculates the prorated amount based on the remaining time in the current billing cycle
  • Charges the customer only for the difference between the old and new plan
  • During a trial period, this will immediately switch the user to the new plan, charging the customer right away

2. full_immediately

  • Charges the customer the full subscription amount for the new plan
  • Ignores any remaining time or credits from the previous plan
  • Useful when you want to reset the billing cycle or charge the full amount regardless of proration

3. difference_immediately

  • When upgrading, the customer is immediately charged the difference between the two plan amounts.
  • For example, if the current plan is 30 Dollars and the customer upgrades to an 80 Dollars, they are charged $50 instantly.
  • When downgrading, the unused amount from the current plan is added as internal credit and automatically applied to future subscription renewals.
  • For example, if the current plan is 50 Dollars and the customer switches to a 20 Dollars plan, the remaining $30 is credited and used toward the next billing cycle.

4. do_not_bill

  • Applies the plan change immediately but does not charge anything at the time of the change.
  • The updated plan (and quantity/add-ons) is billed at the next scheduled renewal, and the original billing date is preserved.
All three “charge now” modes reset the billing cycle. prorated_immediately, difference_immediately, and full_immediately move the subscription’s next_billing_date to the change date. Only do_not_bill keeps the original renewal date, but it applies no immediate charge.

Behavior

  • When you invoke this API, Dodo Payments immediately initiates a charge based on your selected proration option
  • If the plan change is a downgrade and you use prorated_immediately, credits will be automatically calculated and added to the subscription’s credit balance. These credits are specific to that subscription and will only be used to offset future recurring payments of the same subscription
  • The full_immediately option bypasses credit calculations and charges the complete new plan amount
Choose your proration option carefully: Use prorated_immediately for fair billing that accounts for unused time, or full_immediately when you want to charge the complete new plan amount regardless of the current billing cycle.

Charge Processing

  • The immediate charge initiated upon plan change usually completes processing in less than 2 minutes
  • If this immediate charge fails for any reason, the subscription is automatically placed on hold until the issue is resolved

On-Demand Subscriptions

On-demand subscriptions let you charge customers flexibly, not just on a fixed schedule. This feature is available for all accounts.
To create an on-demand subscription: To create an on-demand subscription, use the POST /subscriptions API endpoint and include the on_demand field in your request body. This allows you to authorize a payment method without an immediate charge, or set a custom initial price. To charge an on-demand subscription: For subsequent charges, use the POST /subscriptions//charge endpoint and specify the amount to charge the customer for that transaction.
For a complete, step-by-step guide (including request/response examples, safe retry policies, and webhook handling), see the On-Demand Subscriptions Guide.

Key Things to Know About Subscription Billing

Set the subscription period longer than the payment frequency. If the subscription period equals the payment frequency (e.g. period = 1 month, frequency = 1 month), the subscription is valid for a single cycle and then moves to expired instead of renewing. For an ongoing monthly plan, set a long subscription period (e.g. 20 years) with a monthly payment frequency.
Currency locks at the first successful charge. Always pass billing_currency and billing_address.country explicitly when creating the checkout. If omitted, they’re detected from the customer’s IP (Adaptive Currency), and once the subscription takes its first charge the currency is fixed for its lifetime. A customer who later travels can’t switch it.
Trials take a $0 authorization, not a charge. When a subscription has a trial, the trial start creates a $0 mandate authorization to save the card; the first real charge happens when the trial ends. In the payments list a subscription in trial shows exactly one payment with amount: 0.
Subscription lifecycle: on_hold = a renewal failed (recoverable: prompt the customer to update their payment method; dunning retries apply). expired = the term ended without renewal and cannot be reactivated. The customer must resubscribe. cancelled = ended by the customer or merchant. Most renewal failures are issuer-side declines (insufficient funds, card declined), not a Dodo error.
Indian cards run on an RBI e-mandate. Off-session charges (renewals and plan-change charges) can take up to ~48 hours to settle, and recurring auto-debits above ₹15,000 require fresh customer authentication (so an upgrade crossing that limit can’t ride the existing mandate). While one charge is still processing, a second charge on the same subscription fails with “Cannot create new charge as previous payment is not successful yet.” Non-Indian cards confirm near-instantly.
Subscription charges have a $1 minimum (or currency equivalent). Amounts of $0.01–$0.99 are rejected with product_price: value out of range; only $0 is allowed, via an on-demand mandate_only setup.

Create Subscription

API reference for creating subscription products and managing subscription lifecycle

Change Subscription Plan

API reference for upgrading, downgrading, or changing subscription plans with proration options

Update Payment Method

API reference for updating payment methods and reactivating on-hold subscriptions

Patch Subscription

API reference for updating subscription details and configuration
Last modified on July 29, 2026