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

# Bill for non-catalog items

Charge for an item without adding it to your product catalog by passing price or product attributes when working with a transaction or a subscription.

---

As well as creating transactions for items in [your product catalog](https://developer.paddle.com/build/products/create-products-prices.md), 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](https://developer.paddle.com/api-reference/transactions/overview.md) calculate and capture revenue in Paddle. To bill for an item, you add it to a transaction. You can do this in two ways:

{% feature-comparison level=3 %}

{% feature-column title="Using your product catalog" %}

{% feature-item title="How it works" %}
[Create products and prices in Paddle](https://developer.paddle.com/build/products/create-products-prices.md), then [pass prices IDs to transactions](https://developer.paddle.com/build/transactions/create-transaction.md) or [Paddle.js](https://developer.paddle.com/build/checkout/pass-update-checkout-items.md) to bill for them.
{% /feature-item %}
{% feature-item title="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.
{% /feature-item %}
{% feature-item title="Example use cases" %}
- SaaS companies who sell subscription plans and addons. Prices may [vary by country](https://developer.paddle.com/build/products/offer-localized-pricing.md), but items remain the same.
- Companies who sell a selection of digital products or software licenses where the items remain the same.
{% /feature-item %}

{% /feature-column %}

{% feature-column title="Billing for non-catalog items" %}

{% feature-item title="How it works" %}
Pass price and product attributes directly to a [transaction when creating or updating](https://developer.paddle.com/build/transactions/create-transaction.md) to bill for them.
{% /feature-item %}
{% feature-item title="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.
{% /feature-item %}
{% feature-item title="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.
{% /feature-item %}

{% /feature-column %}

{% /feature-comparison %}

### How do non-catalog items relate to catalog items?

A [complete product](https://developer.paddle.com/build/products/create-products-prices.md) in Paddle is made up of a [product entity](https://developer.paddle.com/api-reference/products/overview.md) that describes the item, and a [related price entity](https://developer.paddle.com/api-reference/prices/overview.md) 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](https://developer.paddle.com/build/transactions/create-transaction.md) with non-catalog items, Paddle creates a price entity and (optionally) a related product entity. They have a [Paddle ID](https://developer.paddle.com/api-reference/about/paddle-ids.md) as normal, meaning you can use the [get a product](https://developer.paddle.com/api-reference/products/get-product.md) or [get a price](https://developer.paddle.com/api-reference/prices/get-price.md) 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](https://developer.paddle.com/api-reference/products/list-products.md) or [prices](https://developer.paddle.com/api-reference/prices/list-prices.md), 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](https://developer.paddle.com/build/subscriptions/bill-add-one-time-charge.md)
- [Update a subscription to add recurring non-catalog items](https://developer.paddle.com/build/subscriptions/add-remove-products-prices-addons.md)

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](https://developer.paddle.com/build/transactions/default-payment-link.md) 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:

1. **Preview transaction** {% badge label="Optional" variant="outline" /%}  
   Preview tax and localized pricing before creating the transaction.
2. **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.

{% callout type="info" %}
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.
{% /callout %}

### Preview transaction {% step=true badge="Optional" %}

Send a `POST` request to the `/transactions/preview` endpoint to preview the transaction.

{% api-example method="POST" path="/transactions/preview" href="/api-reference/transactions/preview-transaction" %}

```json
{
  "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"
}
```

```json
{
  "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"
  }
}
```

{% /api-example %}

### Create transaction {% step=true %}

Send a `POST` request to the `/transactions` endpoint to create the transaction.

The created transaction is `draft`. [Pass it to a checkout](https://developer.paddle.com/build/transactions/pass-transaction-checkout.md) to capture customer and address information.

{% api-example method="POST" path="/transactions" href="/api-reference/transactions/create-transaction" %}

```json
{
  "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"
}
```

```json
{
  "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"
  }
}
```

{% /api-example %}

## 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:

1. **Preview transaction** {% badge label="Optional" variant="outline" /%}  
   Preview tax and localized pricing before creating the transaction.
2. **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.

{% callout type="info" %}
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.
{% /callout %}

### Preview transaction {% step=true stepReset=true badge="Optional" %}

Send a `POST` request to the `/transactions/preview` endpoint to preview the transaction.

{% api-example method="POST" path="/transactions/preview" href="/api-reference/transactions/preview-transaction" %}

```json
{
  "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"
}
```

```json
{
  "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"
  }
}
```

{% /api-example %}

### Create transaction {% step=true %}

Send a `POST` request to the `/transactions` endpoint to create the transaction.

The created transaction is `draft`. [Pass it to a checkout](https://developer.paddle.com/build/transactions/pass-transaction-checkout.md) to capture customer and address information. Paddle automatically creates a subscription for recurring items — use webhooks to [provision your app](https://developer.paddle.com/build/subscriptions/provision-access-webhooks.md).

{% api-example method="POST" path="/transactions" href="/api-reference/transactions/create-transaction" %}

```json
{
  "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"
}
```

```json
{
  "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"
  }
}
```

{% /api-example %}

## 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](https://developer.paddle.com/api-reference/products/update-product.md) or [update a price](https://developer.paddle.com/api-reference/prices/update-price.md) 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](https://developer.paddle.com/build/products/create-products-prices.md)

## 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](https://developer.paddle.com/build/products/create-products-prices.md) 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.

{% api-example method="PATCH" path="/products/{product_id}" href="/api-reference/products/update-product" %}

```json
{
  "type": "standard"
}
```

```json
{
  "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"
  }
}
```

{% /api-example %}