Trials let customers try your product before they commit to paying. You can offer a free trial, where customers aren't charged during the trial period, or a paid trial, where customers pay a reduced amount for the trial period before the subscription renews at the full price.
This tutorial walks through creating a subscription with a trial, from creating a price with a trial period to managing the trial lifecycle. It works for both free and paid trials. Cardless trials are in early access, so they're not covered here yet.
This tutorial covers trials that collect a payment method at signup. To let customers start a trial without entering payment details, see Create a cardless trial
What are we building?
We'll create a subscription plan with a trial period using Paddle Checkout. When a customer completes their purchase:
- For a free trial, they enter payment details but aren't charged until the trial ends.
- For a paid trial, they enter payment details, then they're charged the trial price right away and the base price when the trial ends.
In both cases, Paddle creates a subscription with the status trialing, then automatically renews it at the base price when the trial ends.
We'll learn how to:
- Create a price for a product that has a trial period, choosing whether it's free or paid
- Sell the trial using Paddle Checkout, which automatically creates a subscription
- Provision access when Paddle creates the subscription
- Manage and convert the trial throughout its lifecycle
How it works
Whether a subscription has a trial is determined by whether the prices on the subscription have a trial period. A trial is an attribute of a price entity, set using the trial_period object.
There are three kinds of trials:
- Free trials
Customers enter payment details but aren't charged until the trial ends. - Paid trials
Customers pay a reduced amount for the trial period, then the full price when the trial ends. - Cardless trials
Customers start the trial without entering payment details. Cardless trials are in early access.
For a paid trial, the trial price and the recurring base price share the same price_id. You can set the trial period unit price using the unit_price field against the trial_period object.
From checkout to renewal
This diagram shows how a trial is billed, from checkout through to the first renewal.
flowchart TD
A[Customer completes checkout for a price with a trial] --> B{Trial has a unit price?}
B -->|No, free trial| C[No charge<br>payment method stored]
B -->|Yes, paid trial| D[Customer charged the trial price]
C --> E[Subscription created<br><pre>status: trialing</pre>]
D --> E
E --> F[Trial period ends]
F --> G[Paddle charges the base price<br><pre>status: active</pre>]
Overview
Create and sell a trial in four steps:
- Create a price with a trial period
Create a price for a product that has a trial period. Choose whether the trial is free or paid. - Pass the price to a checkout
Pass the price to Paddle Checkout. On completion, Paddle automatically creates a subscription. - Handle fulfillment
Provision access to your app and store the subscription when Paddle creates it. - Manage and convert the trial
Update, extend, or activate the trial, then let it convert to a paying subscription.
Before you begin
- Create a product
Prices relate to products. Create a product to describe what you're selling, then keep its Paddle ID to relate a price to it. - Set up Paddle Checkout
You'll collect payment details using Paddle Checkout. Include and initialize Paddle.js and set a default payment link.
Create a price
Create a price for your product that includes a trial_period object with an interval and frequency. Trials require a billing_cycle, so the price must be recurring.
To make the trial free, leave out the trial price.
To make the trial paid, add a trial_period.unit_price with the amount to charge for the trial period. When setting a paid trial price:
- The trial price currency must match the base price currency, otherwise Paddle returns a
trial_currency_mismatcherror. - You can use unit price overrides to localize the trial price, but you must provide a matching trial price override for the same countries and currency as the base price overrides.
This example creates a monthly price with a 7-day paid trial that charges $1.00 for the trial, then $15.00 on renewal.
{ "product_id": "pro_01k5c106wy997av8jmz1qfng2q", "description": "Monthly (per seat) with 7 day paid trial", "name": "Monthly (per seat)", "trial_period": { "interval": "day", "frequency": 7, "unit_price": { "amount": "100", "currency_code": "USD" } }, "billing_cycle": { "interval": "month", "frequency": 1 }, "unit_price": { "amount": "1500", "currency_code": "USD" }}{ "data": { "id": "pri_01k5c14mgh9dc3wgk3vb23p0t7", "product_id": "pro_01k5c106wy997av8jmz1qfng2q", "type": "standard", "description": "Monthly (per seat) with 7 day paid trial", "name": "Monthly (per seat)", "billing_cycle": { "interval": "month", "frequency": 1 }, "trial_period": { "interval": "day", "frequency": 7, "requires_payment_method": true, "unit_price": { "amount": "100", "currency_code": "USD" }, "unit_price_overrides": [] }, "tax_mode": "account_setting", "unit_price": { "amount": "1500", "currency_code": "USD" }, "unit_price_overrides": [], "custom_data": null, "status": "active", "quantity": { "minimum": 1, "maximum": 100 }, "import_meta": null, "created_at": "2026-06-10T09:00:00.000000Z", "updated_at": "2026-06-10T09:00:00.000000Z" }, "meta": { "request_id": "a1f4c2e0-8b3d-4f7a-9c21-6d0b5e9a3c14" }}import { Paddle, Environment } from '@paddle/paddle-node-sdk';
// Initialize the Paddle SDK with your API key and environment.const paddle = new Paddle('YOUR_API_KEY', { environment: Environment.sandbox // or Environment.production});
async function createPrice() { try { const newPrice = await paddle.prices.create({ productId: 'pro_01k5c106wy997av8jmz1qfng2q', description: 'Monthly (per seat) with 7 day paid trial', name: 'Monthly (per seat)', trialPeriod: { interval: 'day', frequency: 7, unitPrice: { amount: '100', currencyCode: 'USD', }, }, billingCycle: { interval: 'month', frequency: 1, }, unitPrice: { amount: '1500', currencyCode: 'USD', }, });
console.log('Successfully created price:', newPrice.id); } catch (error) { console.error('Error creating price:', error); }}
createPrice();Extract the Paddle ID of the price you create โ you'll need this to pass to a checkout in the next step.
Pass the price to a checkout
Paddle Checkout is the simplest way to collect payment for a trial. When the checkout is completed, Paddle automatically creates a subscription for the items on the transaction.
Open a checkout by passing items to Paddle.Checkout.open(), or build a pricing page that opens a checkout for the price.
Paddle.Checkout.open({ items: [{ priceId: "pri_01k5c14mgh9dc3wgk3vb23p0t7", quantity: 1 }],});Paddle Checkout handles the trial workflow for you, presenting a compliant signup based on the customer's jurisdiction. For a paid trial, the checkout shows the trial price and length, and the customer is charged the trial price to complete checkout. For a free trial, the customer enters payment details but isn't charged.
Display trial pricing in an inline checkout
Inline checkout lets you build branded checkout experiences that are fully integrated with your app. You're responsible for displaying the order summary using the data Paddle.js passes to your eventCallback.
To build a compliant inline checkout, you need to display the trial price and length, the recurring price and interval, and the order subtotal, tax, and total due today. You can use the following fields from the Paddle.js event data:
| Checkout field | Paddle.js event field |
|---|---|
| Trial length (for example, a 7-day trial) | data.items[].trial_period: interval and frequency |
| Recurring price and interval (for example, $192/month) | data.items[].recurring_totals and data.items[].billing_cycle |
| Item line prices, using the non-trial price | data.items[].recurring_totals |
| Order subtotal, tax, and total due today | data.totals |
Render each line item from its recurring_totals to show the ongoing price, and the order summary from data.totals to show what the customer pays now.
It's important that customers know who they're buying from, what they're buying, and how much they're paying.
To build an inline checkout that's compliant and optimized for conversion, your implementation must include:
- If recurring, how often it recurs and the total to pay on renewal. If a trial, how long the trial lasts.
- A description of what's being purchased.
- Transaction totals, including subtotal, total tax, and grand total. Be sure to include the currency too.
- The full inline checkout frame, including the checkout footer that has information about Paddle, our terms of sale, and our privacy policy.
- A link to your refund policy, if it differs from the Paddle.com standard refund policy.
Handle fulfillment
Paddle automatically creates a subscription with the status trialing once the checkout transaction is completed. Fulfillment for trials is the same as for other subscriptions:
- Create a webhook endpoint and create notification destinations for subscription and transaction events.
- Listen for the
subscription.createdandtransaction.completedevents. - Extract and store the
subscription_id, then grant the appropriate level of access to your app.
For full details, see Handle provisioning and fulfillment.
Determine the trial type
You might want to handle each trial type differently in your app. For example, you might want to surface the trial price for a paid trial or limit the features available for free trials. Check the trial_period on the subscription item's price to tell which type of trial a trialing subscription is on.
flowchart TD
Start{Is <pre>status = trialing</pre>?}
Start -->|No| NotTrial[Not a trial]
Start -->|Yes| PM{Subscription item price <pre>requires_payment_method</pre>?}
PM -->|false| Cardless[Cardless trial<br>always free]
PM -->|true| Price{Subscription item price <pre>trial_period.</pre><pre>unit_price</pre> set?}
Price -->|No| Free[Card-required free trial]
Price -->|Yes| Paid[Card-required paid trial]
Check these fields against the price for each item in a subscription entity:
| Field | Description |
|---|---|
status | trialing is used for all kinds of trial. |
items[].price.trial_period | null if the price has no trial period. |
items[].price.trial_period.requires_payment_method | true for card-required trials, false for cardless trials. |
items[].price.trial_period.unit_price | Set for paid trials, null for free trials. |
This gives you three trial types:
- Card-required free trial:
requires_payment_methodistrueandunit_priceisnull. - Card-required paid trial:
requires_payment_methodistrueandunit_priceis set. - Cardless trial:
requires_payment_methodisfalse. Cardless trials are always free.
Manage and convert the trial
While a subscription is trialing, you can update items, extend the trial, or activate it early.
Since the subscription isn't on its full billing cycle yet, proration doesn't apply. Use do_not_bill as the proration_billing_mode when making changes.
Add or remove items, change quantities, and bill for one-time charges.
Give customers more time, or cut the trial short to start billing.
When the trial period ends, Paddle charges the base price to the payment method on file and transitions the subscription to active.
Events
price.created | Occurs when you create a price with a trial period. |
transaction.completed | Occurs when the checkout for a trial is completed. |
subscription.created | Occurs when Paddle creates a subscription for the trial. |
subscription.trialing | Occurs when a subscription enters its trial period. |
subscription.activated | Occurs when a trial converts to a full-price paying subscription. |
Common errors
price_trial_period_requires_billing_cycle |
| Occurs when your request contains trial_period but billing_cycle is not set. |
price_trial_period_missing_fields |
| Occurs when your request contains trial_period but interval and frequency are not present. |
price_trial_period_frequency_below_1 |
| Occurs when your supplied value for trial_period.frequency is below 1. |
price_trial_period_frequency_greater_than_maximum |
| Occurs when your supplied value for trial_period.frequency is greater than 999. |
trial_currency_mismatch |
| Occurs when the currency of a trial price doesn't match the currency of the associated price. |
trial_is_either_paid_or_cardless |
| Occurs when a trial has a unit price but doesn't require a payment method. |