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

# Line Item Products

> Add products to a line item's product pool and remove products from it.

export const EndpointBadge = ({method = "GET", children}) => {
  const METHOD_STYLES = {
    GET: {
      bg: "mint-bg-[#2AB673]"
    },
    POST: {
      bg: "mint-bg-[#3064E3]"
    },
    PUT: {
      bg: "mint-bg-[#C28C30]"
    },
    PATCH: {
      bg: "mint-bg-[#DA622B]"
    },
    DELETE: {
      bg: "mint-bg-[#CB3A32]"
    },
    API: {
      bg: "mint-bg-black"
    }
  };
  const key = method.toUpperCase();
  const styles = METHOD_STYLES[key] ?? METHOD_STYLES.API;
  return <div className="relative mt-7">
      <span className={`absolute -top-2 -left-2 z-10 ${styles.bg} text-white px-2.5 py-0.5 rounded-full text-xs font-bold tracking-wide`}>
        {key}
      </span>
      {children}
    </div>;
};

## Overview

The product pool of a line item holds the products that the line item can promote. Adding a product makes it available to the line item; it does not assign it to a Creative Product Collection. Removing a product takes it out of the pool, so the line item stops promoting it.

Adding products is the step that follows creating a line item with [Line Item Core](/retail-media/experimental/docs/line-items-core). Identify eligible products to add by accessing your account [catalog](/retail-media/experimental/docs/catalogs). Products can be removed from the pool at any point in the line item's lifecycle.

<Info>
  The long-term goal is to support product operations across line item types. Initial documented support targets Onsite Display Auction line items and uses `productType` `DisplayProduct`. Additional product types will be documented as support is added.
</Info>

### Relationship to the current API

These endpoints replace the equivalent routes documented in [Promoted Products](/retail-media/docs/promoted-products), which remain available on stable versions in the meantime.

| Experimental endpoint | Route it replaces |
| - | - |
| `POST /line-items/{lineItemId}/products/add` | `POST /line-items/{lineItemId}/products/append` |
| `POST /line-items/{lineItemId}/products/delete` | `POST /line-items/{lineItemId}/products/delete` on stable versions |

<Warning>
  The removal route uses the same path on Experimental and on stable versions, but the request body is different. On `experimental`, send the `productIds` body described below. On a stable version, keep sending the array of promoted product resources documented in [Promoted Products](/retail-media/docs/promoted-products).
</Warning>

## Endpoints

| Method | Endpoint | Description |
| - | - | - |
| `POST` | `/line-items/{lineItemId}/products/add` | Add products to a line item's product pool. |
| `POST` | `/line-items/{lineItemId}/products/delete` | Remove products from a line item's product pool. |

## Add Products to a Line Item

Adds one or more products to the product pool of an existing line item.

<EndpointBadge method="post">
  ```http theme={null}
  https://api.criteo.com/experimental/retail-media/line-items/{lineItemId}/products/add
  ```
</EndpointBadge>

### Path parameters

| Parameter | Required | Description |
| - | - | - |
| `lineItemId` | Yes | External ID of the line item that owns the product pool. |

### Request attributes

| Attribute | Required | Description |
| - | - | - |
| `productType` | Yes | Type of products being added. It must match the type of the target line item. Only `DisplayProduct` is currently supported. |
| `displayProductDetails` | Yes | Products to add, when `productType` is `DisplayProduct`. At least one entry is required, and the number of entries is subject to the product limit configured for the line item's campaign type. |
| `displayProductDetails[].productId` | Yes | ID of the product to add. It must exist in the retailer catalog of the line item's account. |

The resource `type` is `Products`. Do not send response-only fields such as `approvalStatus` inside `displayProductDetails`.

### Behavior

* All items in the request must be added successfully; otherwise, no items are added to the product pool. If any `productId` is invalid, the entire request fails and the product pool is left unchanged.
* A `productId` is invalid if it is missing, not present in the retailer catalog, marked as deleted, or unavailable in the line item's account.
* Adding a `productId` that is already in the product pool is a no-op. No duplicate is created and no error is returned for that product.
* Duplicate `productId` values within the same request are deduplicated. The product is added once.
* Products are added to the product pool only. They are not assigned to a Creative Product Collection.

### Sample Request

<Accordion title="Example request">
  ```json theme={null}
  {
    "data": {
      "type": "Products",
      "attributes": {
        "productType": "DisplayProduct",
        "displayProductDetails": [
          {
            "productId": "sku-12345"
          },
          {
            "productId": "sku-67890"
          }
        ]
      }
    }
  }
  ```
</Accordion>

### Sample Response

<Accordion title="Example response (200)">
  ```json theme={null}
  {
    "data": {
      "type": "Products",
      "attributes": {
        "productType": "DisplayProduct",
        "displayProductDetails": [
          {
            "productId": "sku-12345",
            "approvalStatus": "Unsubmitted"
          },
          {
            "productId": "sku-67890",
            "approvalStatus": "Unsubmitted"
          }
        ]
      }
    }
  }
  ```
</Accordion>

### Errors

| HTTP status | Code | Description |
| - | - | - |
| `400` | `product-invalid` | One or more of the supplied product IDs are invalid. |
| `400` | `products-required` | No product was supplied, or the list of products is empty. |
| `400` | `products-limit-exceeded` | More products were supplied than the line item permits. |
| `400` | `invalid-line-item-type` | The requested `productType` does not match the type of the target line item. |
| `401` | `unauthenticated` | The caller is not authenticated. |
| `403` | `forbidden` | The caller cannot modify the target line item, or the line item does not exist. |
| `501` | `unsupported-line-item-type` | The line item buy type is not Auction, which the current implementation does not support. |
| `503` | `catalog-unavailable` | The catalog service could not be reached to validate the products. Retry the request later. |

## Remove Products from a Line Item

Removes one or more products from the product pool of an existing line item.

<EndpointBadge method="post">
  ```http theme={null}
  https://api.criteo.com/experimental/retail-media/line-items/{lineItemId}/products/delete
  ```
</EndpointBadge>

### Path parameters

| Parameter | Required | Description |
| - | - | - |
| `lineItemId` | Yes | External ID of the line item that owns the product pool. |

### Request attributes

| Attribute | Required | Description |
| - | - | - |
| `productIds` | Yes | IDs of the products to remove from the product pool. At least one ID is required, and a single request accepts at most 1500 IDs. |
| `productType` | No | Type of products being removed. It must match the type of the target line item. Defaults to `DisplayProduct`, which is the only value currently supported. |

The resource `type` is `Products`. Unlike the add operation, products are supplied as a flat list of IDs in `productIds`, not as objects in a details array.

<Info>
  The contract also accepts `SponsoredProduct` as a `productType`. Only `DisplayProduct` on Onsite Display Auction line items is supported by the service today. Other combinations are rejected.
</Info>

### Behavior

* Removal is a soft delete. The product's status becomes `Removed` and the line item stops promoting it. The product is not erased, and it remains in your account catalog.
* Products are removed from both the current line item and its proposal, so a pending change set stays consistent with the live one.
* The operation is idempotent. Removing a `productId` that is not in the product pool succeeds, removes nothing for that ID, and is reported as a warning. Repeating the same request has no further effect.
* Duplicate `productId` values within the same request are deduplicated. Each product is removed once.
* Validation is all-or-nothing. If any `productId` is malformed, or the request exceeds 1500 IDs, the entire request is rejected and the product pool is left unchanged.
* Products are removed from the product pool only. They are not yet removed from Creative Product Collections that reference them; that behavior will follow.
* Removing every product from a line item is allowed. The line item may stop being served as a result.

### Sample Request

<Accordion title="Example request">
  ```json theme={null}
  {
    "data": {
      "type": "Products",
      "attributes": {
        "productType": "DisplayProduct",
        "productIds": [
          "sku-12345",
          "sku-67890"
        ]
      }
    }
  }
  ```
</Accordion>

### Sample Responses

When all requested products are removed without warnings, the endpoint returns `200` with an empty outcome.

<Accordion title="Example response (200)">
  ```json theme={null}
  {
    "errors": [],
    "warnings": []
  }
  ```
</Accordion>

When one or more product IDs are not associated with the line item, the response includes a warning for each unmatched ID. Matching products are still removed.

<Accordion title="Example response with warning (200)">
  ```json theme={null}
  {
    "errors": [],
    "warnings": [
      {
        "traceId": "unique-request-id",
        "code": "unmatched-product-id",
        "type": "validation",
        "title": "unmatched-product-id",
        "detail": "Product ID 'product-1' is not in the line item's product pool",
        "instance": "@data/attributes/productIds/1"
      }
    ]
  }
  ```
</Accordion>

The response does not list the products that were removed. To confirm the resulting state of the pool, read the line item's products with `GET /line-items/{lineItemId}/products`.

<Accordion title="Example error response (400)">
  ```json theme={null}
  {
    "warnings": [],
    "errors": [
      {
        "traceId": "1a2b3c4d5e6f7890",
        "code": "products-limit-exceeded",
        "type": "validation",
        "title": "Products limit exceeded",
        "detail": "Payload limit exceeded: request contains 1501 products, exceeds max allowed 1500 per request.",
        "instance": "/experimental/retail-media/line-items/{lineItemId}/products/delete"
      }
    ]
  }
  ```
</Accordion>

### Errors

| HTTP status | Code | Description |
| - | - | - |
| `400` | `product-invalid` | One or more of the supplied product IDs are invalid. No products are removed. |
| `400` | `products-limit-exceeded` | More than 1500 product IDs were supplied. No products are removed. |
| `400` | `invalid-line-item-type` | The target line item's type is not supported, or `productType` does not match the type of the target line item. |
| `401` | `unauthenticated` | The caller is not authenticated. |
| `403` | `forbidden` | The caller cannot modify the target line item. |
| `403` | `line-item-not-found` | The line item does not exist. A `403` is returned rather than a `404` so that the API does not reveal whether a line item exists to a caller that is not authorized for it. |
| `500` | `internal-error` | The request could not be completed because of an unexpected service failure. |
| `503` | `catalog-unavailable` | The catalog service could not be reached to validate the products. Retry the request later. |
