As well as creating transactions for items in your product catalog, you can create transactions for non-catalog items. This is useful for one-off or bespoke items that are specific to that transaction. For example, you may agree a custom price with an enterprise customer.
You may also like to bill for non-catalog items if you work with products where the price changes often, or where you need to manage your product catalog outside of Paddle. For example, games companies typically manage their product catalog centrally because they need to work with app stores.
How it works
Transactions calculate and capture revenue in Paddle. To bill for an item, you add it to a transaction. You can do this in two ways:
Using your product catalog
How it works
Create products and prices in Paddle, then pass prices IDs to transactions or Paddle.js to bill for them.
Key features
- Manage items using the product catalog in Paddle.
- Items can be reused across transactions easily.
- Useful for companies who sell a set of digital products at the same price points.
Example use cases
- SaaS companies who sell subscription plans and addons. Prices may vary by country, but items remain the same.
- Companies who sell a selection of digital products or software licenses where the items remain the same.
Billing for non-catalog items
How it works
Pass price and product attributes directly to a transaction when creating or updating to bill for them.
Key features
- Manage items using your own product database.
- Items are specific to a transaction.
- Useful for companies with lots of items, or where item prices may change a lot.
Example use cases
- Games companies who maintain a large catalog of items and may show different prices to different user segments.
- eBook retailers, where publishers set prices and they may change daily.
How do non-catalog items relate to catalog items?
A complete product in Paddle is made up of a product entity that describes the item, and a related price entity that describes how much and how often a product is billed.
You can add non-catalog items to a transaction where:
- Only the price is custom.
This is great where the products you offer stay the same, but you might offer bespoke pricing from time to time. Your non-catalog price relates an existing catalog product entity in Paddle, sharing the same product name, image, and tax category. - Both the price and the product are custom.
Where you manage your product catalog outside of Paddle, you can create entirely custom products. Your item uses a non-catalog price and a non-catalog product.
When you create or update a transaction with non-catalog items, Paddle creates a price entity and (optionally) a related product entity. They have a Paddle ID as normal, meaning you can use the get a product or get a price operations to work with them, but they're not added to your product catalog.
This means they're not returned by default when listing products or prices, and they're not shown in the Paddle dashboard.
Non-catalog price and product entities have a type of custom, so you can differentiate between entities in your catalog.
Subscriptions
This guide walks through adding non-catalog items to transactions, but you can also:
- Bill one-time non-catalog items to a subscription
- Update a subscription to add recurring non-catalog items
You can configure non-catalog items for a subscriptions in the same way as transactions.
Before you begin
To create a transaction, you'll need to first set your default payment link under Paddle > Checkout > Checkout settings > Default payment link and get it approved.
Bill for a non-catalog price for an existing product
You can add a non-catalog price for an existing product in your catalog to a transaction. In this case, the product a customer is purchasing is the same, but you have a specific price for it.
Bill for a non-catalog price using the API in two steps:
- Preview transaction Optional
Preview tax and localized pricing before creating the transaction. - Create transaction
Send a request to create the transaction with your non-catalog item.
In your request, build an items array with price objects and quantity for each item. Relate your custom price to an existing catalog product using product_id.
You can also include existing catalog items using price_id and quantity.
Include customer_id and address_id (and optionally business_id) to create a ready transaction.
Recurring items on a transaction must have the same billing interval. For example, you can't have a transaction with some prices that are billed monthly and some products that are billed annually.
Preview transaction Optional
Send a POST request to the /transactions/preview endpoint to preview the transaction.
{ "items": [ { "quantity": 1, "price": { "product_id": "pro_01he5kwnnvgdv2chtpgavk2rf8", "description": "New user price (FTUE)", "name": "Invigaron Berries welcome price", "unit_price": { "amount": "999", "currency_code": "USD" } } } ], "currency_code": "USD"}{ "data": { "customer_id": null, "address_id": null, "business_id": null, "currency_code": "USD", "address": null, "customer_ip_address": null, "discount_id": null, "items": [ { "price": { "id": null, "description": "New user price (FTUE)", "type": "custom", "name": "Invigaron Berries welcome price", "product_id": "pro_01he5kwnnvgdv2chtpgavk2rf8", "billing_cycle": null, "trial_period": null, "tax_mode": "account_setting", "unit_price": { "amount": "999", "currency_code": "USD" }, "unit_price_overrides": null, "custom_data": null, "quantity": { "minimum": 1, "maximum": 100 }, "status": "active" }, "quantity": 1, "proration": null, "include_in_totals": true } ], "details": { "tax_rates_used": [ { "tax_rate": "0", "totals": { "subtotal": "999", "discount": "0", "tax": "0", "total": "999" } } ], "totals": { "subtotal": "999", "tax": "0", "discount": "0", "total": "999", "grand_total": "999", "fee": null, "credit": "0", "credit_to_balance": "0", "balance": "999", "earnings": null, "currency_code": "USD" }, "line_items": [ { "price_id": "pri_01hj3rny6ayaczq16m477qrg81", "quantity": 1, "totals": { "subtotal": "999", "tax": "0", "discount": "0", "total": "999" }, "product": { "id": "pro_01he5kwnnvgdv2chtpgavk2rf8", "name": "Invigaron Berries Hoard", "description": "Level up!", "type": "standard", "tax_category": "standard", "image_url": "https://paddle-sandbox.s3.amazonaws.com/user/10889/XBWZPsQoSc6YyViK5ocI_fire.png", "custom_data": null, "status": "active" }, "tax_rate": "0", "unit_totals": { "subtotal": "999", "tax": "0", "discount": "0", "total": "999" } } ] }, "ignore_trials": false, "available_payment_methods": [] }, "meta": { "request_id": "5615f928-3486-4933-b3f1-f70246c3b625" }}Create transaction
Send a POST request to the /transactions endpoint to create the transaction.
The created transaction is draft. Pass it to a checkout to capture customer and address information.
{ "items": [ { "quantity": 1, "price": { "product_id": "pro_01he5kwnnvgdv2chtpgavk2rf8", "description": "New user price (FTUE)", "name": "Invigaron Berries welcome price", "unit_price": { "amount": "999", "currency_code": "USD" } } } ], "currency_code": "USD"}{ "data": { "id": "txn_01hj3rtynv8rdn1zbcjk42z05j", "status": "draft", "customer_id": null, "address_id": null, "business_id": null, "custom_data": null, "origin": "api", "collection_mode": "automatic", "subscription_id": null, "invoice_id": null, "invoice_number": null, "billing_details": null, "billing_period": null, "currency_code": "USD", "discount_id": null, "created_at": "2023-12-20T14:07:25.454915616Z", "updated_at": "2023-12-20T14:07:25.454915616Z", "billed_at": null, "items": [ { "price": { "id": "pri_01hj3rtypd6wns6g943kbndswg", "description": "New user price (FTUE)", "type": "custom", "name": "Invigaron Berries welcome price", "product_id": "pro_01he5kwnnvgdv2chtpgavk2rf8", "billing_cycle": null, "trial_period": null, "tax_mode": "account_setting", "unit_price": { "amount": "999", "currency_code": "USD" }, "unit_price_overrides": [], "custom_data": null, "quantity": { "minimum": 1, "maximum": 100 }, "status": "active" }, "quantity": 1 } ], "details": { "tax_rates_used": [ { "tax_rate": "0", "totals": { "subtotal": "999", "discount": "0", "tax": "0", "total": "999" } } ], "totals": { "subtotal": "999", "tax": "0", "discount": "0", "total": "999", "grand_total": "999", "fee": null, "credit": "0", "credit_to_balance": "0", "balance": "999", "earnings": null, "currency_code": "USD" }, "adjusted_totals": { "subtotal": "999", "tax": "0", "total": "999", "grand_total": "999", "fee": "0", "earnings": "0", "currency_code": "USD" }, "payout_totals": null, "adjusted_payout_totals": null, "line_items": [ { "id": "txnitm_01hj3rtyqzfh4nf7rb9fs2y8xx", "price_id": "pri_01hj3rtypd6wns6g943kbndswg", "quantity": 1, "totals": { "subtotal": "999", "tax": "0", "discount": "0", "total": "999" }, "product": { "id": "pro_01he5kwnnvgdv2chtpgavk2rf8", "name": "Invigaron Berries Hoard", "description": "Level up!", "type": "standard", "tax_category": "standard", "image_url": "https://paddle-sandbox.s3.amazonaws.com/user/10889/XBWZPsQoSc6YyViK5ocI_fire.png", "custom_data": null, "status": "active" }, "tax_rate": "0", "unit_totals": { "subtotal": "999", "tax": "0", "discount": "0", "total": "999" } } ] }, "payments": [], "checkout": { "url": "https://aeroedit.com/pay?_ptxn=txn_01hj3rtynv8rdn1zbcjk42z05j" } }, "meta": { "request_id": "a62a5fb8-3f63-4222-9b57-68e16dada004" }}Bill for a non-catalog price and a non-catalog-product
You can add a non-catalog price for a non-catalog product in your catalog to a transaction. This is useful if you manage your product catalog outside of Paddle, or you want to sell something entirely bespoke.
Bill for a non-catalog price and product using the API in two steps:
- Preview transaction Optional
Preview tax and localized pricing before creating the transaction. - Create transaction
Send a request to create the transaction with your non-catalog price and product.
In your request, build an items array with price objects (each including a nested product object) and quantity for each item.
You can also include existing catalog items using price_id and quantity.
Include customer_id and address_id (and optionally business_id) to create a ready transaction.
Recurring items on a transaction must have the same billing interval. For example, you can't have a transaction with some prices that are billed monthly and some products that are billed annually.
Preview transaction Optional
Send a POST request to the /transactions/preview endpoint to preview the transaction.
{ "items": [ { "quantity": 1, "price": { "description": "Battle pass", "name": "Monthly", "billing_cycle": { "interval": "month", "frequency": 1 }, "unit_price": { "amount": "1099", "currency_code": "USD" }, "product": { "name": "Invigaron VIP pass", "tax_category": "standard", "description": "Lock in 200x Invigaron Berries a month, plus faster gem spawns, exclusive skins, and early access to the leaderboard." } } } ], "currency_code": "USD"}{ "data": { "customer_id": null, "address_id": null, "business_id": null, "currency_code": "USD", "address": null, "customer_ip_address": null, "discount_id": null, "items": [ { "price": { "id": null, "description": "Battle pass", "type": "custom", "name": "Monthly", "product_id": null, "billing_cycle": { "interval": "month", "frequency": 1 }, "trial_period": null, "tax_mode": "account_setting", "unit_price": { "amount": "1099", "currency_code": "USD" }, "unit_price_overrides": null, "custom_data": null, "quantity": { "minimum": 1, "maximum": 100 }, "status": "active" }, "quantity": 1, "proration": null, "include_in_totals": true } ], "details": { "tax_rates_used": [ { "tax_rate": "0", "totals": { "subtotal": "1099", "discount": "0", "tax": "0", "total": "1099" } } ], "totals": { "subtotal": "1099", "tax": "0", "discount": "0", "total": "1099", "grand_total": "1099", "fee": null, "credit": "0", "credit_to_balance": "0", "balance": "1099", "earnings": null, "currency_code": "USD" }, "line_items": [ { "price_id": "pri_01hj3sbfnr0t7eat8na0pqyapv", "quantity": 1, "totals": { "subtotal": "1099", "tax": "0", "discount": "0", "total": "1099" }, "product": { "id": null, "name": "Invigaron VIP pass", "description": "Lock in 200x Invigaron Berries a month, plus faster gem spawns, exclusive skins, and early access to the leaderboard.", "type": "custom", "tax_category": "standard", "image_url": null, "custom_data": null, "status": "active" }, "tax_rate": "0", "unit_totals": { "subtotal": "1099", "tax": "0", "discount": "0", "total": "1099" } } ] }, "ignore_trials": false, "available_payment_methods": [] }, "meta": { "request_id": "4459e3d0-1d65-4f9f-8b34-1e3f3faf5cbd" }}Create transaction
Send a POST request to the /transactions endpoint to create the transaction.
The created transaction is draft. Pass it to a checkout to capture customer and address information. Paddle automatically creates a subscription for recurring items — use webhooks to provision your app.
{ "items": [ { "quantity": 1, "price": { "description": "Battle pass", "name": "Monthly", "billing_cycle": { "interval": "month", "frequency": 1 }, "unit_price": { "amount": "1099", "currency_code": "USD" }, "product": { "name": "Invigaron VIP pass", "tax_category": "standard", "description": "Lock in 200x Invigaron Berries a month, plus faster gem spawns, exclusive skins, and early access to the leaderboard." } } } ], "currency_code": "USD"}{ "data": { "id": "txn_01hj3sct9my6jsx9zt55thzpfw", "status": "draft", "customer_id": null, "address_id": null, "business_id": null, "custom_data": null, "origin": "api", "collection_mode": "automatic", "subscription_id": null, "invoice_id": null, "invoice_number": null, "billing_details": null, "billing_period": null, "currency_code": "USD", "discount_id": null, "created_at": "2023-12-20T14:17:10.858871798Z", "updated_at": "2023-12-20T14:17:10.858871798Z", "billed_at": null, "items": [ { "price": { "id": "pri_01hj3sctd36bwevs47dw8jxdkn", "description": "Battle pass", "type": "custom", "name": "Monthly", "product_id": "pro_01hj3sctbh6r2hyga7qg29dznq", "billing_cycle": { "interval": "month", "frequency": 1 }, "trial_period": null, "tax_mode": "account_setting", "unit_price": { "amount": "1099", "currency_code": "USD" }, "unit_price_overrides": [], "custom_data": null, "quantity": { "minimum": 1, "maximum": 100 }, "status": "active" }, "quantity": 1 } ], "details": { "tax_rates_used": [ { "tax_rate": "0", "totals": { "subtotal": "1099", "discount": "0", "tax": "0", "total": "1099" } } ], "totals": { "subtotal": "1099", "tax": "0", "discount": "0", "total": "1099", "grand_total": "1099", "fee": null, "credit": "0", "credit_to_balance": "0", "balance": "1099", "earnings": null, "currency_code": "USD" }, "adjusted_totals": { "subtotal": "1099", "tax": "0", "total": "1099", "grand_total": "1099", "fee": "0", "earnings": "0", "currency_code": "USD" }, "payout_totals": null, "adjusted_payout_totals": null, "line_items": [ { "id": "txnitm_01hj3scte0r426j1nbaheptrtj", "price_id": "pri_01hj3sctd36bwevs47dw8jxdkn", "quantity": 1, "totals": { "subtotal": "1099", "tax": "0", "discount": "0", "total": "1099" }, "product": { "id": "pro_01hj3sctbh6r2hyga7qg29dznq", "name": "Invigaron VIP pass", "description": "Lock in 200x Invigaron Berries a month, plus faster gem spawns, exclusive skins, and early access to the leaderboard.", "type": "custom", "tax_category": "standard", "image_url": null, "custom_data": null, "status": "active" }, "tax_rate": "0", "unit_totals": { "subtotal": "1099", "tax": "0", "discount": "0", "total": "1099" } } ] }, "payments": [], "checkout": { "url": "https://aeroedit.com/pay?_ptxn=txn_01hj3sct9my6jsx9zt55thzpfw" } }, "meta": { "request_id": "69845039-96b9-4ab5-a339-bc21dc401cee" }}Update a non-catalog price or product
Non-catalog products and prices are created for specific transactions. They're not considered part of your product catalog. You shouldn't ordinarily need to update them.
Non-catalog products and prices have Paddle IDs, so you can update them using the update a product or update a price operations if needed. For example, you might correct a spelling error in a name or description — especially where an item is recurring.
To learn more, see Create products and prices
Add a non-catalog item to your catalog
If you find yourself adding similar non-catalog prices or products to transactions, you might like to add a custom item you've previously worked with to your product catalog.
We recommend that you create a new product or price in your catalog where you're adding an item to your standard offering.
You can also get an existing custom price or product using its ID, then change the type to standard.
Set type to standard to add the item to your product catalog. The type field exists against both product and price entities.
{ "type": "standard"}{ "data": { "id": "pro_01hj3sctbh6r2hyga7qg29dznq", "name": "Invigaron VIP pass", "tax_category": "standard", "type": "standard", "description": "Lock in 200x Invigaron Berries a month, plus faster gem spawns, exclusive skins, and early access to the leaderboard.", "image_url": null, "custom_data": null, "status": "active", "import_meta": null, "created_at": "2023-12-20T14:17:10.769Z", "updated_at": "2023-12-20T14:18:35.093Z" }, "meta": { "request_id": "dc625fb7-38b8-47b5-8497-fe51e5c1a2e3" }}Events
transaction.created | Occurs when a transaction is created. |
price.created | Occurs when a price is created. |
product.created | Occurs when a product is created. |