Skip to main content

Prerequisites

Before you start, you need:
  • A Dodo Payments merchant account
  • An API key from Developer → API Keys in the dashboard, stored in DODO_PAYMENTS_API_KEY
  • A webhook secret from Developer → Webhooks, stored in DODO_PAYMENTS_WEBHOOK_KEY
  • At least one subscription product created under Products
For more details, see Integration Guide Prerequisites.

API Integration

Checkout Sessions

Create a subscription by building a checkout session with your subscription product. The customer authorizes a payment method and the subscription activates when they complete checkout.
You can combine subscription products with one-time products in the same checkout session. This enables setup fees, hardware bundles with SaaS, and similar use cases. See Checkout Sessions for examples.

API Response

The response includes a checkout_url:
Redirect the customer to this URL. They authorize the payment method and the subscription activates.

Webhooks

Webhooks notify your server when subscription events occur. Set up your endpoint under Developer → Webhooks in the dashboard. To set up your webhook endpoint, see Webhooks.

Subscription Event Types

Track these events to manage the subscription lifecycle:
  1. subscription.active — Subscription is activated
  2. subscription.updated — A field on the subscription changed
  3. subscription.on_hold — A renewal or plan-change charge failed
  4. subscription.failed — Subscription creation failed (terminal; customer must resubscribe)
  5. subscription.renewed — A recurring charge succeeded
  6. subscription.past_due — A renewal failed and the grace period started; the customer keeps access until past_due_ends_at
  7. subscription.plan_changed — The plan was upgraded, downgraded, or changed
  8. subscription.cancelled — The subscription was cancelled
  9. subscription.expired — The subscription reached the end of its term
These are the core events. For the full list, including paused, unpaused, and update_payment_method, see Subscription Webhooks.
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 webhook sequence depends on whether the subscription 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. If your business has a grace period, a failed renewal first moves the subscription to past_due (subscription.past_due), and it moves to on_hold (or cancelled, depending on your grace period settings) only when the grace period ends. See Subscription States.
  • 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 four options for handling the immediate charge:

1. prorated_immediately

  • Credits the unused portion of the current billing cycle, prorated by the time remaining. The credit covers the base plan, quantity, and any add-ons
  • Then charges a full cycle at the new plan, quantity, and add-ons. The charge itself is never prorated
  • Net immediate charge = (full new cycle) minus (remaining fraction x full old cycle). If the credit is larger, the difference is held as subscription-scoped credit for future renewals
  • 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 with no credit for the previous cycle
  • On upgrade or downgrade alike, the customer pays the entire new plan price from scratch
  • Useful when you want to charge the full amount regardless of how much time was left on the old plan

3. difference_immediately

  • The customer pays only the gap between the old plan price and the new plan price
  • The amount does not depend on when in the cycle the change is made. The same upgrade costs the same on day 1 and on day 29
  • When upgrading, the customer is immediately charged the difference. For example, $30/month → $80/month = $50 charged instantly
  • When downgrading, the price difference is stored as subscription-scoped credit and applied automatically to future renewals. For example, $50/month → $20/month = $30 stored as credit

4. do_not_bill

  • Applies the plan change immediately but does not charge anything at the time of the change. The new plan, quantity, and add-ons are usable straight away
  • Because nothing is charged now, an upgrade gives the customer the higher plan free for the remainder of the current cycle. A downgrade takes effect immediately with no credit for the unused portion of the cycle they have already paid for
  • Add-ons granted through do_not_bill are not credited on a later plan change, because they were never billed. A subsequent change bills the new add-on quantity in full
  • 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
  • With prorated_immediately, a credit for the unused portion of the current cycle is calculated on every change, upgrade or downgrade alike. If that credit exceeds the new cycle charge, the remainder is 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
  • With difference_immediately, the net is always the exact price difference. For downgrades, the excess is stored as subscription-scoped credit, the same as prorated_immediately
  • The full_immediately option bypasses credit calculations and charges the complete new plan amount
  • The do_not_bill option applies the change immediately but defers billing to the next renewal date, which is preserved
Choosing a proration mode:
  • difference_immediately — customer pays the price difference. The most predictable option; the charge is the same regardless of when in the cycle the change is made.
  • prorated_immediately — customer is credited only for unused time on the current cycle. The charge varies depending on when in the cycle the change happens.
  • full_immediately — customer pays the full new plan amount. No credit for the previous cycle.
  • do_not_bill — no charge now. The new plan is billed at the next renewal. The only mode that preserves the original billing date.

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 /checkouts API endpoint and include the subscription_data.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.
POST /subscriptions is deprecated. It still works for existing integrations, but new integrations should create on-demand subscriptions through a Checkout Session (POST /checkouts) with subscription_data.on_demand. See the On-Demand Subscriptions Guide for the current flow.
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 a free trial shows exactly one payment with total_amount of 0. A paid trial charges its trial_amount upfront instead.
Subscription lifecycle: past_due = a renewal failed and the grace period is running (the customer keeps access). 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. A subscription product priced at exactly $0 is allowed; see Card-Optional at Zero Price. To authorize a card without charging it, use an on-demand mandate_only setup.

Create Subscription (Deprecated)

Legacy API for creating a subscription directly. Use Checkout Sessions for new integrations

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
Lần sửa đổi cuối 25 tháng 9, 2026