> For the complete documentation index, see [llms.txt](https://developer.paddle.com/llms.txt).

# Port your subscription data from Paddle Classic to Paddle Billing

Map your product catalog and migrate your subscription data from Classic to Billing using screens in the dashboard. Available when you've built an integration with Paddle Billing.

---

After you've built an integration with Paddle Billing, use the dashboard to port subscription data from Paddle Classic to Paddle Billing. You can update records in your database, then turn off your Paddle Classic integration.

This guide walks through how to use the migration screens, and how to handle migrated data.

{% callout type="default" %}
Access to Migrations in the dashboard is limited to users who are part of our early access program. If you're interested in joining the program, review this guide then [fill out the form](https://developer.paddle.com/changelog/2025/classic-to-billing-migrations.md) to apply. We'll reach out when space is available if you meet the program requirements.
{% /callout %}

## How it works

You can use screens in the Paddle dashboard to migrate data from Paddle Billing to Paddle Classic. It's a multistep process, with a final chance to review everything at the end.

You can run as many migrations as you want, choosing how many subscriptions you want to migrate each time.

### Product catalog mapping

In the first step of migration, you can [map plans](#map-products-and-prices) in Paddle Classic with [products](https://developer.paddle.com/api-reference/products/overview.md) in Paddle Billing.

You can map the same product to multiple plans. For example, you might have separate plans in Paddle Classic for monthly and annual pricing, which you could map to the same product in Paddle Billing.

For each plan that you map to a product, Paddle creates [a new price](https://developer.paddle.com/api-reference/prices/overview.md) for that product. You can review these on [step two of the migration screen](#map-products-and-prices).

Price overrides in Paddle Classic and Paddle Billing can't be mapped one-to-one. If you have currency override prices in Paddle Classic, Paddle creates a new price in Paddle Billing for each override price.

By default, the new override prices are created as [non-catalog prices](https://developer.paddle.com/build/transactions/bill-create-custom-items-prices-products.md). This means they're considered specific to the subscriptions that they're used on, and not presented in the dashboard or returned by the API by default.

### Subscription selection

After you've mapped products and prices, you can choose which [subscriptions](https://developer.paddle.com/api-reference/subscriptions/overview.md) you want to migrate.

We recommend choosing a small number of subscriptions for your first migration, so you can familiarize yourself with the process and check that your new Paddle Billing integration flows work correctly.

You can run as many migrations as you want, so you can go plan-by-plan, migrate a number at a time, or migrate all.

You can only migrate active subscriptions. Where subscriptions are past due, wait for dunning to complete then follow-up with another migration when subscriptions are active.

### During migration

During a migration, Paddle creates new records in Paddle Billing and cancels the subscription in Paddle Classic. This is how the process works:

1. **Prices imported.**  
   New prices are created in Paddle Billing for any mapped products.
2. **Customer data imported.**  
   A [customer](https://developer.paddle.com/api-reference/customers/overview.md) and an [address](https://developer.paddle.com/api-reference/addresses/overview.md) entity are created in Paddle Billing for each subscription. Where an existing customer exists, Paddle creates a new address for that customer instead.
3. **Business data imported** {% badge label="Optional" variant="outline" /%}  
   If a customer has a tax or VAT number, a [business](https://developer.paddle.com/api-reference/businesses/overview.md) entity is created for the customer in Paddle Billing.
4. **Subscriptions imported.**  
   A [subscription](https://developer.paddle.com/api-reference/subscriptions/overview.md) entity is created in Paddle Billing for each subscription. It's linked to the customer, address, and business created earlier, and the products and prices mapped initially.
5. **Subscription canceled in Paddle Classic.**  
  The subscription is canceled silently in Paddle Classic, with no disruption to the customer. If there's a problem importing a subscription to Paddle Billing, it's not canceled in Paddle Classic.

### Post-migration

You can track the status of a migration in the dashboard. Paddle emails you when a migration is completed.

As part of the migration process, you need to create or update records in your database for the subscriptions you ported over. You can either:

- **Use webhooks** {% badge label="Recommended" /%}  
  Listen for [`customer.imported`](https://developer.paddle.com/webhooks/customers/customer-imported.md), [`subscription.imported`](https://developer.paddle.com/webhooks/subscriptions/subscription-imported.md), and [other imported webhooks](#related-notifications), then create records in your database.
- **Create a database migration script.**  
  Export a list of migrated subscriptions from the dashboard, then use this to write a script to create records in your database.

{% callout type="info" %}
As part of migration, `imported` events occur in place of `created` events. For example, [`customer.imported`](https://developer.paddle.com/webhooks/customers/customer-imported.md) occurs in place of `customer.created`.
{% /callout %}

In most cases, we recommend using webhooks because they contain all the information you need. You might like to create a database migration script if you're importing a large number of subscriptions that might overwhelm your webhook endpoint.

If you're using a migration script, you may need to make additional calls to the Paddle API to fetch the data you need.

## Before you begin

### Build an integration with Paddle Billing

Paddle Billing is built on [an entirely new API](https://developer.paddle.com/api-reference/overview.md), with [new webhooks](https://developer.paddle.com/webhooks/overview.md), [Paddle.js library](https://developer.paddle.com/paddle-js/overview.md), and [SDKs](https://developer.paddle.com/resources/overview.md).

Before you can port your subscriptions from Paddle Classic to Paddle Billing, you'll need to build an integration with Paddle Billing so that you're ready to run subscriptions through Paddle Billing rather than Paddle Classic.

To learn more, see [Reintegration checklist](https://developer.paddle.com/migrate/paddle-classic/checklist.md).

### Complete a transaction

You'll need to have at least one completed transaction on Paddle Billing to show that your integration is ready.

Make sure you've had one new customer signup through your Paddle Billing integration, or launch a checkout and take a payment through it yourself to verify that your Paddle Billing integration works correctly.

### Subscribe to imported events

We recommend using [webhooks](https://developer.paddle.com/webhooks/overview.md) to [handle fulfillment and provisioning](https://developer.paddle.com/build/subscriptions/provision-access-webhooks.md) for subscription lifecycle events. Build a [webhook handler function](https://developer.paddle.com/webhooks/respond-to-webhooks.md) and [set up a notification destination](https://developer.paddle.com/webhooks/notification-destinations.md) in Paddle.

If you're using webhooks to handle your post-migration workflow, subscribe to [`customer.imported`](https://developer.paddle.com/webhooks/customers/customer-imported.md) and [`subscription.imported`](https://developer.paddle.com/webhooks/subscriptions/subscription-imported.md) events.

To learn more, see [Create or update notification destinations](https://developer.paddle.com/webhooks/notification-destinations.md) and [Handle webhook delivery](https://developer.paddle.com/webhooks/respond-to-webhooks.md).

{% callout type="note" %}
You can use [webhook simulator](https://developer.paddle.com/webhooks/simulator.md) to check that your webhook endpoint and fulfillment workflows are working correctly.
{% /callout %}

## Prepare your database

Create or update records in your database for new subscriptions in Paddle Billing.

To avoid data contamination, we recommend creating new tables in your database for subscription data rather than enriching existing records.

Customers can have more than one subscription in Paddle Billing, so we recommend creating a table for customer data and a separate table for subscription data, then relating them using the customer ID.

{% accordion %}
{% accordion-item title="Recommended fields for a customer table" %}
As part of the migration, [`customer.imported`](https://developer.paddle.com/webhooks/customers/customer-imported.md) occurs in Paddle Billing. You should store:

| Description       | Field name                         | Reason to store                                                             |
|-------------------|------------------------------------|-----------------------------------------------------------------------------|
| Customer ID       | `customer.id`                      | Used to identify a customer and relate to record in the subscription table. |
| Email address     | `customer.email`                   | Used to identify a customer.                                                |
| Classic reference | `customer.import_meta.external_id` | Used to match this subscription to a Paddle Classic user record.            |

{% /accordion-item %}

{% accordion-item title="Recommended fields for a subscription table" %}
As part of the migration, [`subscription.imported`](https://developer.paddle.com/webhooks/subscriptions/subscription-imported.md) occurs in Paddle Billing. You should store:

| Description           | Field name                                                       | Reason to store                                                                                                                                             |
|-----------------------|------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Customer ID           | `subscription.customer_id`                                       | Used to relate this record with a record in the customer table.                                                                                             |
| Subscription ID       | `subscription.id`                                                | Used to identify this subscription and work with this subscription using the API.                                                                           |
| Subscription status   | `subscription.status`                                            | Used to limit or stop access when paused or canceled, or determine if a subscription is past due or trialing.                                               |
| Subscription items    | `subscription.items[].price.id`, `subscription.items[].quantity` | Used to change items on a subscription as part of an upgrade or downgrade workflow.                                                                         |
| Subscription products | `subscription.items[].price.product_id`                          | Used to determine which features in your app a customer should have access to.                                                                              |
| Collection mode       | `subscription.collection_mode`                                   | Used to determine whether a subscription bills automatically or whether Paddle sends an invoice for charges that customers must pay manually.               |
| Scheduled change      | `subscription.scheduled_change`                                  | Used to determine whether a subscription is scheduled to pause or cancel. You can't change items on a subscription when there's a pending scheduled change. |
| Classic reference     | `subscription.import_meta.external_id`                           | Used to match this subscription to a Paddle Classic subscription record.                                                                                    |

{% /accordion-item %}

{% /accordion %}

## Migrate subscription data

### Get started

{% instruction-steps %}

1. Choose the **Paddle Billing** option in the toggle in the nav bar.
2. Go to **Paddle > Migrate**.
3. Click **Get started**.

{% callout type="warning" %}
You'll need to have at least one completed transaction to be able to start a migration.
{% /callout %}

{% /dashboard-instructions %}

### Map products and prices

{% instruction-steps %}

1. Use the drop-down to map plans in Classic to products in Billing. Paddle creates a new price for each plan against the product you select here.
2. Click **Continue**, then review prices.
3. Click **Price details** next to any price to update the name or description of a price. You can change other details later.
4. Click **Continue**.

{% /dashboard-instructions %}

### Select subscriptions

{% instruction-steps %}

1. Use the checkboxes to select subscriptions that you want to migrate. You can search, sort, and filter using the options at the top.
2. Click **Continue**.

{% callout type="info" %}
We recommend selecting a small number of subscriptions for your first migration, so you can check your new integration works.
{% /callout %}

{% /dashboard-instructions %}

### Review and start

{% instruction-steps %}

1. Review the products and subscriptions you're migrating, then click **Start migration**.
2. Complete the final check by clicking **Start migration**.

{% /dashboard-instructions %}

## Handle migrated subscriptions

{% accordion %}
{% accordion-item title="Webhooks" %}

If you built a post-migration workflow using [`customer.imported`](https://developer.paddle.com/webhooks/customers/customer-imported.md) and [`subscription.imported`](https://developer.paddle.com/webhooks/subscriptions/subscription-imported.md) events, you should check that:

- You received webhooks for imported events. Paddle automatically queues events that failed for retry, using an exponential backoff schedule. You can view logs in the Paddle dashboard or [using the API](https://developer.paddle.com/api-reference/notification-logs/list-notification-logs.md).
- You processed webhooks successfully, and records are created in your database. You can export a list of subscriptions from **Paddle > Migrate** and check against your records.
- You're running subscriptions in Paddle Billing through your Paddle Billing integration.

When all your subscriptions are ported to Paddle Billing, you can safely remove your Paddle Classic integration.

{% /accordion-item %}
{% accordion-item title="Migration script" %}

If you're using a database migration script to create and update records in your database, you should:

1. Toggle **Paddle Billing** in the nav bar, then export a list of subscriptions from **Paddle > Migrate**.
2. Write a database migration script to create or update records in your database.
3. Check that you're running subscriptions in Paddle Billing through your Paddle Billing integration.

When all your subscriptions are ported to Paddle Billing, you can safely remove your Paddle Classic integration.
{% /accordion-item %}
{% /accordion %}