> ## Documentation Index
> Fetch the complete documentation index at: https://docs.leavo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Product Catalog

> Reusable products, shared by every pipeline in the account

The product catalog is **global per account** — it is shared by every pipeline, never
scoped to just one of them.

<Info>
  A deal also accepts **one-off** lines (`custom: true`), which exist only inside it and never
  enter the global catalog. See
  [Create a deal](/en/api-reference/pipeline/deals#create-a-deal).
</Info>

## The Product object

<ResponseField name="id" type="string">
  Product UUID
</ResponseField>

<ResponseField name="name" type="string">
  Product name
</ResponseField>

<ResponseField name="price" type="number">
  Default unit price
</ResponseField>

***

## List products

<Card>
  <strong>GET</strong> `/backend/pipeline/products`
</Card>

Requires `pipeline.view`.

<RequestExample>
  ```bash cURL theme={null}
  curl -X GET "https://api.leavo.ai/backend/pipeline/products" \
    -H "Authorization: Bearer your_api_key_here"
  ```
</RequestExample>

<ResponseExample>
  ```json 200 OK theme={null}
  [
    {
      "id": "ff0e8400-e29b-41d4-a716-446655440000",
      "name": "Premium Plan",
      "price": 1500.00
    }
  ]
  ```
</ResponseExample>

***

## Create a product

<Card>
  <strong>POST</strong> `/backend/pipeline/products`
</Card>

Requires `pipeline.products.manage`.

### Request Body

<ParamField body="name" type="string" required>
  Product name
</ParamField>

<ParamField body="price" type="number">
  Unit price. Default: `0`.
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://api.leavo.ai/backend/pipeline/products" \
    -H "Authorization: Bearer your_api_key_here" \
    -H "Content-Type: application/json" \
    -d '{ "name": "Premium Plan", "price": 1500.00 }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.leavo.ai/backend/pipeline/products', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer your_api_key_here',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ name: 'Premium Plan', price: 1500.00 })
  });
  ```
</RequestExample>

Returns **201 Created** with the product.

***

## Update a product

<Card>
  <strong>PUT</strong> `/backend/pipeline/products/{id}`
</Card>

Requires `pipeline.products.manage`.

<ParamField body="name" type="string" required>
  Product name
</ParamField>

<ParamField body="price" type="number">
  Unit price
</ParamField>

<Note>
  Changing the price in the catalog does **not** change existing deals. Name and price are copied
  into the line at the moment the product is added to the deal, preserving the historical value
  of the negotiation.
</Note>

***

## Delete a product

<Card>
  <strong>DELETE</strong> `/backend/pipeline/products/{id}`
</Card>

Requires `pipeline.products.manage`.

<Warning>
  Deleting a product **does not break the deals that already use it**. Existing lines become
  one-off lines (`custom: true`, `product_id: null`), preserving name and price — the deals stay
  editable and the total does not change.
</Warning>

<ResponseExample>
  ```json 200 OK theme={null}
  { "success": true }
  ```
</ResponseExample>
