From 8f97a72a9677a5d2f1d34c70a72baad576a412fc Mon Sep 17 00:00:00 2001 From: Jared Hoover Date: Tue, 12 May 2026 14:22:22 -0500 Subject: [PATCH 01/13] Bulk API limits - ACCS-703 --- src/pages/get-started/api-security.md | 4 ++++ src/pages/rest/use-rest/bulk-endpoints.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/pages/get-started/api-security.md b/src/pages/get-started/api-security.md index ab753e8e5..cfd610bb9 100644 --- a/src/pages/get-started/api-security.md +++ b/src/pages/get-started/api-security.md @@ -108,6 +108,10 @@ By default, any one of these arrays can include up to 20 items, but you can chan ## Input limit for REST endpoints + + +In Adobe Commerce as a Cloud Service, Bulk API limits are determined by the **Maximum Entities per Bulk Request** setting in the [Store Configuration](https://experienceleague.adobe.com/en/docs/commerce-admin/config/general/bulk-api). + Some REST endpoints can contain a high number of elements, and developers need a way to set the limit for each endpoint. The limit for a specific REST endpoint can be set in the `webapi.xml` configuration file for synchronous requests and `webapi_async.xml` for asynchronous requests. diff --git a/src/pages/rest/use-rest/bulk-endpoints.md b/src/pages/rest/use-rest/bulk-endpoints.md index 68f3e4a71..54660170b 100644 --- a/src/pages/rest/use-rest/bulk-endpoints.md +++ b/src/pages/rest/use-rest/bulk-endpoints.md @@ -13,6 +13,10 @@ Bulk API endpoints differ from other REST endpoints in that they combine multipl ​ In Adobe Commerce as a Cloud Service, message queues run automatically. There is no need to manage queues or install a message broker. + + +In Adobe Commerce as a Cloud Service, Bulk API limits are determined by the **Maximum Entities per Bulk Request** setting in the [Store Configuration](https://experienceleague.adobe.com/en/docs/commerce-admin/config/general/bulk-api). + ​ Cron jobs are the default mechanism for [managing message queues](https://experienceleague.adobe.com/en/docs/commerce-operations/configuration-guide/message-queues/manage-message-queues) and starting message queue [consumers](https://experienceleague.adobe.com/en/docs/commerce-operations/configuration-guide/message-queues/consumers), but you can also use external process control systems (like [Supervisor](https://supervisord.readthedocs.io/en/latest/)) to monitor process management. You can use the [`bin/magento queue:consumers:start async.operations.all`](https://experienceleague.adobe.com/docs/commerce-operations/configuration-guide/cli/start-message-queues.html) command to manually start the `async.operations.all` consumer that handles asynchronous and bulk API messages. However, manually starting consumers is not recommended because it requires you to keep your terminal session connected. From 6e2cb6e3d37796961e24ef4f1ad13d82fdec29ff Mon Sep 17 00:00:00 2001 From: Sangmi Lee Date: Thu, 1 Oct 2026 12:02:20 +0200 Subject: [PATCH 02/13] docs: document catalog price rule REST APIs --- src/pages/config.md | 1 + .../catalog-price-rules/index.md | 352 ++++++++++++++++++ src/pages/rest/saas-integrations/index.md | 1 + 3 files changed, 354 insertions(+) create mode 100644 src/pages/rest/saas-integrations/catalog-price-rules/index.md diff --git a/src/pages/config.md b/src/pages/config.md index 80b6409c9..e5861c986 100644 --- a/src/pages/config.md +++ b/src/pages/config.md @@ -156,6 +156,7 @@ - [Multicoupon](/rest/modules/multicoupon/index.md) - [Sales refunds](/rest/modules/sales/index.md) - [SaaS integrations](/rest/saas-integrations/index.md) + - [Catalog price rules](/rest/saas-integrations/catalog-price-rules/index.md) - [Custom email](/rest/saas-integrations/custom-email/index.md) - [Gift card accounts](/rest/saas-integrations/gift-card-accounts/index.md) - [Login as Customer](/rest/saas-integrations/login-as-customer/index.md) diff --git a/src/pages/rest/saas-integrations/catalog-price-rules/index.md b/src/pages/rest/saas-integrations/catalog-price-rules/index.md new file mode 100644 index 000000000..ce8344608 --- /dev/null +++ b/src/pages/rest/saas-integrations/catalog-price-rules/index.md @@ -0,0 +1,352 @@ +--- +title: Catalog Price Rule REST Endpoints +description: Learn how to create, retrieve, update, delete, and search catalog price rules with REST APIs in Adobe Commerce as a Cloud Service. +keywords: + - REST + - Integration +--- + + + +# Catalog price rule API + +The catalog price rule REST API lets integrations manage product discounts without creating or editing each rule in the Admin. Use the API to discover supported conditions, create rules, and manage existing rules with the same nested condition logic available in the Admin. + +Catalog price rules apply to products for the specified websites and customer groups. For cart price rules, which apply discounts during checkout, use the [salesRules API](https://adobe-commerce-saas.redoc.ly/tag/salesRules/). The two APIs use different condition payloads. + +## Authentication and scope + +Authenticate each request with an Adobe Identity Management Service (IMS) access token. The associated Admin role must include the `Magento_CatalogRule::promo_catalog` Access Control List (ACL) resource. Customer and guest access is not supported. + +See [REST authentication](../../authentication/index.md) for user and server-to-server authentication. + +Use the Adobe Commerce as a Cloud Service URL structure: + +```http +GET https://.api.commerce.adobe.com//V1/catalogPriceRules/metadata +Authorization: Bearer +Content-Type: application/json +Store: all +``` + +Do not include `/rest` or a store view code in the URL. The `Store` header specifies the request scope, while the rule's `website_ids` specifies the websites where the discount applies. Include `website_ids` in the rule payload. + +## REST API reference + +All six endpoints require the `Magento_CatalogRule::promo_catalog` ACL resource. + +| Method | Endpoint | Description | +| --- | --- | --- | +| `GET` | `/V1/catalogPriceRules/metadata` | Discover discount actions and attributes available for conditions. | +| `GET` | `/V1/catalogPriceRules/search` | Search rules with filters, sorting, and pagination. | +| `GET` | `/V1/catalogPriceRules/{ruleId}` | Retrieve a rule by ID. | +| `POST` | `/V1/catalogPriceRules` | Create a rule. | +| `PUT` | `/V1/catalogPriceRules/{ruleId}` | Update a rule. | +| `DELETE` | `/V1/catalogPriceRules/{ruleId}` | Delete a rule. | + +## Discover available conditions + +Before creating a rule, retrieve the metadata: + +```http +GET /V1/catalogPriceRules/metadata +``` + +The response contains `simple_actions`, the supported discount actions, and `condition_attributes`, the attributes that can be used in rule conditions. Each attribute includes its `attribute_code`, `label`, `input_type`, supported `operators`, and a `value_source` endpoint when values must be retrieved separately. + +The following response excerpt shows the category condition: + +```json +{ + "simple_actions": [ + "by_percent", + "by_fixed", + "to_percent", + "to_fixed" + ], + "condition_attributes": [ + { + "attribute_code": "category_ids", + "label": "Category", + "input_type": "select", + "operators": ["==", "!=", "{}", "!{}", "()", "!()"], + "value_source": "/V1/categories" + } + ] +} +``` + +The available product attributes depend on the instance's attribute configuration. Use the returned metadata instead of assuming that an attribute or operator is supported. + +Retrieve the IDs needed for rule scope and condition values using the following endpoints. These endpoints have their own permission requirements. + +| Values | Endpoint | +| --- | --- | +| Website IDs | `GET /V1/store/websites` | +| Customer group IDs | `GET /V1/customerGroups/search` | +| Category IDs | `GET /V1/categories` | +| Product attribute set IDs | `GET /V1/products/attribute-sets/sets/list` | +| Attribute option values | `GET /V1/products/attributes/{attributeCode}/options` | + +Use option values, not option labels, in conditions for select and multiselect attributes. + +## Rule fields + +Create and update requests wrap the rule fields in a `rule` object. The following requirements and defaults apply when creating a rule. Updates preserve omitted or null fields. + +| Field | Type | Description | +| --- | --- | --- | +| `rule_id` | Integer | The generated rule ID returned by the API. For updates, use the ID in the URL. | +| `name` | String | Required. Must not be blank. | +| `description` | String | Optional rule description. | +| `website_ids` | Integer array | Required. A nonempty list of existing storefront website IDs. Website ID `0` is not supported. | +| `customer_group_ids` | Integer array | Required. A nonempty list of existing customer group IDs. | +| `simple_action` | String | Required. One of the four supported discount actions. | +| `discount_amount` | Number | Required. Must be nonnegative. Percentage actions accept values from 0 to 100. | +| `is_active` | Integer | `0` for inactive or `1` for active. Defaults to `0`. | +| `stop_rules_processing` | Integer | `1` prevents subsequent rules from applying to matching products. `0` allows further rule processing. Defaults to `1`. | +| `sort_order` | Integer | Nonnegative rule priority. Lower values have higher priority. Defaults to `0`. | +| `from_date` | String | Optional start date or UTC datetime. | +| `to_date` | String | Optional end date or UTC datetime. Must not precede `from_date`. | +| `condition` | Object | Optional root condition group containing leaf conditions or nested groups. Omitting it on create targets all products within the rule's website and customer group scope. | + +### Discount actions + +The `simple_action` determines how `discount_amount` changes the price: + +| Action | Behavior | Example for a price of 100 | +| --- | --- | --- | +| `by_percent` | Reduce the price by the specified percentage. | An amount of `20` produces a price of 80. | +| `by_fixed` | Reduce the price by the specified fixed amount. | An amount of `20` produces a price of 80. | +| `to_percent` | Set the price to the specified percentage. | An amount of `20` produces a price of 20. | +| `to_fixed` | Set the price to the specified fixed amount. | An amount of `20` produces a price of 20. | + +### Schedule dates + +Both `from_date` and `to_date` accept: + +- `YYYY-MM-DD`. The start date uses the beginning of the day and the end date uses the end of the day in the configured Admin timezone. +- `YYYY-MM-DD HH:MM:SS`. The datetime is interpreted as UTC. + +Schedule values are stored and returned as UTC datetimes. To clear an existing schedule boundary in an update, send an empty string for that field. Omitting the field or sending `null` preserves its current value. + +## Create a rule + +The following request creates an inactive rule that reduces prices by 10 percent for products in category `12`, on website `1`, for customer group `1`. Replace these IDs with existing values from your instance. + +```http +POST /V1/catalogPriceRules +``` + +Request body: + +```json +{ + "rule": { + "name": "Category discount", + "description": "Ten percent off products in the selected category.", + "website_ids": [1], + "customer_group_ids": [1], + "simple_action": "by_percent", + "discount_amount": 10, + "condition": { + "aggregator": "all", + "match": true, + "conditions": [ + { + "attribute_code": "category_ids", + "operator": "==", + "value": "12" + } + ] + } + } +} +``` + +The response returns the saved rule object, including its generated `rule_id` and default values: + +```json +{ + "rule_id": 42, + "name": "Category discount", + "description": "Ten percent off products in the selected category.", + "is_active": 0, + "website_ids": [1], + "customer_group_ids": [1], + "simple_action": "by_percent", + "discount_amount": 10, + "stop_rules_processing": 1, + "sort_order": 0, + "condition": { + "aggregator": "all", + "match": true, + "conditions": [ + { + "attribute_code": "category_ids", + "operator": "==", + "value": "12" + } + ] + } +} +``` + +Review the saved rule before activating it with `is_active: 1`. You can also set `is_active` explicitly when creating a rule. + +## Build nested conditions + +The `condition` object is a root group. Each group has a `conditions` array containing leaf conditions, nested groups, or both. + +- `aggregator: "all"` combines children with AND. `"any"` combines them with OR. The default is `"all"`. +- `match` specifies the required result for each child: `true` or `false`. The default is `true`. The `aggregator` determines whether all or any children must have that result. For example, `"all"` with `match: false` requires every child condition to be false. +- A leaf uses `attribute_code`, `operator`, and a string `value`. Do not include `aggregator`, `match`, or child `conditions` on a leaf. +- A nested group uses `aggregator`, `match`, and child `conditions`. Do not include leaf fields on a group. + +The following update targets products in attribute set `4` that belong to either category `12` or category `13`. Replace these IDs with valid values from your instance. + +```http +PUT /V1/catalogPriceRules/42 +``` + +Request body: + +```json +{ + "rule": { + "condition": { + "aggregator": "all", + "match": true, + "conditions": [ + { + "attribute_code": "attribute_set_id", + "operator": "==", + "value": "4" + }, + { + "aggregator": "any", + "match": true, + "conditions": [ + { + "attribute_code": "category_ids", + "operator": "==", + "value": "12" + }, + { + "attribute_code": "category_ids", + "operator": "==", + "value": "13" + } + ] + } + ] + } + } +} +``` + +Condition trees support up to 10 levels and 100 total nodes, including the root group. Nested groups must contain at least one child. + +Use only operators returned for the attribute by the metadata endpoint. Where an operator supports multiple values, supply a comma-separated string, such as `"12,13"`, rather than a JSON array. Scalar operators for structured values require a single value. The `<=>` operator means "is undefined" and does not require a value. + +## Retrieve and update a rule + +Retrieve a rule by its ID: + +```http +GET /V1/catalogPriceRules/42 +``` + +The response returns the rule object, including its full condition tree. + +Updates preserve existing values for fields you omit or set to `null`. For example, the following request activates the rule and changes the discount to 15 percent without changing its name, scope, schedule, or conditions: + +```http +PUT /V1/catalogPriceRules/42 +``` + +Request body: + +```json +{ + "rule": { + "is_active": 1, + "discount_amount": 15 + } +} +``` + +The response returns the updated rule object. The API validates the combined existing and requested values. For example, updating `discount_amount` to `150` on a `by_percent` rule fails even when `simple_action` is omitted. + +If you provide `condition`, it replaces the entire condition tree. Omitting `condition` or sending `null` preserves the existing tree. To remove all product conditions, send an empty root group: + +```json +{ + "rule": { + "condition": { + "aggregator": "all", + "match": true, + "conditions": [] + } + } +} +``` + +An empty root group makes the rule apply to all products within its website and customer group scope. It is not a way to disable the rule. To disable it, set `is_active` to `0`. + +## Search rules + +Use `/V1/catalogPriceRules/search`, not the collection URL, to list and filter rules. The following request finds active rules, sorts them by ID, and returns the first page of 20 results: + +```text +GET /V1/catalogPriceRules/search + ?searchCriteria[filterGroups][0][filters][0][field]=is_active + &searchCriteria[filterGroups][0][filters][0][value]=1 + &searchCriteria[filterGroups][0][filters][0][conditionType]=eq + &searchCriteria[sortOrders][0][field]=rule_id + &searchCriteria[sortOrders][0][direction]=ASC + &searchCriteria[pageSize]=20 + &searchCriteria[currentPage]=1 +``` + +The query parameters are shown on separate lines for readability. Send them as one URL. + +The response contains: + +- `items`, an array of rule objects, including their condition trees. +- `search_criteria`, the criteria used for the request. +- `total_count`, the total number of matching rules across all pages. + +Increase `searchCriteria[currentPage]` to retrieve subsequent pages. An omitted or zero page size defaults to 200. Values above 500 are capped at 500. Negative page sizes and current pages below 1 are rejected. + +See [Search using REST endpoints](../../use-rest/performing-searches.md) for filter groups, comparison operators, sorting, and pagination. + +## Delete a rule + +```http +DELETE /V1/catalogPriceRules/42 +``` + +The response confirms deletion: + +```json +true +``` + +## Validation + +Invalid rule input returns HTTP `400`. Validation checks required fields, discount actions and amounts, flags, schedule dates, and the existence of referenced websites and customer groups. + +Conditions are checked against the metadata's allowed attributes and operators. Referenced categories, product attribute sets, and option values must be valid. Boolean condition values use `"0"` or `"1"`, and date condition values accept `YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS`. + +A supplied root `condition` must be an object with a `conditions` array. Malformed nodes, empty nested groups, and condition trees that exceed the depth or node limits are rejected. + +Updates that activate a rule or keep it active also validate its existing conditions. If a referenced category, attribute set, or option has been removed, correct the condition before activating or updating the rule. + +Retrieving, updating, or deleting a nonexistent rule returns HTTP `404`. + +## Storefront price updates + +Only active rules within their schedule affect storefront prices for the specified websites and customer groups. + +Price updates are asynchronous. A successful create, update, or delete response confirms the rule operation, not that storefront prices have already changed. Allow time for the price update to complete before checking prices through Catalog Service GraphQL, using the applicable customer group context. diff --git a/src/pages/rest/saas-integrations/index.md b/src/pages/rest/saas-integrations/index.md index 5e6151745..549531f72 100644 --- a/src/pages/rest/saas-integrations/index.md +++ b/src/pages/rest/saas-integrations/index.md @@ -9,6 +9,7 @@ keywords: Review the following topics to learn more about REST APIs available only on Adobe Commerce as a Cloud Service: +- [Catalog price rules](catalog-price-rules/index.md) - [Custom email](custom-email/index.md) - [Gift card accounts](gift-card-accounts/index.md) - [Login as Customer](login-as-customer/index.md) From f320232926a921c84f4c21e31a2d066f7fcb43c6 Mon Sep 17 00:00:00 2001 From: Sangmi Lee Date: Thu, 1 Oct 2026 12:13:07 +0200 Subject: [PATCH 03/13] docs: clarify catalog rule create and update requirements --- .../saas-integrations/catalog-price-rules/index.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pages/rest/saas-integrations/catalog-price-rules/index.md b/src/pages/rest/saas-integrations/catalog-price-rules/index.md index ce8344608..56be9db87 100644 --- a/src/pages/rest/saas-integrations/catalog-price-rules/index.md +++ b/src/pages/rest/saas-integrations/catalog-price-rules/index.md @@ -29,7 +29,7 @@ Content-Type: application/json Store: all ``` -Do not include `/rest` or a store view code in the URL. The `Store` header specifies the request scope, while the rule's `website_ids` specifies the websites where the discount applies. Include `website_ids` in the rule payload. +Do not include `/rest` or a store view code in the URL. The `Store` header specifies the request scope, while the rule's `website_ids` specifies the websites where the discount applies. Include `website_ids` when creating a rule. On update, omit it or send `null` to preserve the existing websites. ## REST API reference @@ -92,17 +92,17 @@ Use option values, not option labels, in conditions for select and multiselect a ## Rule fields -Create and update requests wrap the rule fields in a `rule` object. The following requirements and defaults apply when creating a rule. Updates preserve omitted or null fields. +Create and update requests wrap the rule fields in a `rule` object. Defaults apply when creating a rule. On update, omitted or null fields preserve their existing values. | Field | Type | Description | | --- | --- | --- | | `rule_id` | Integer | The generated rule ID returned by the API. For updates, use the ID in the URL. | -| `name` | String | Required. Must not be blank. | +| `name` | String | Required on create; optional on update. Must not be blank. | | `description` | String | Optional rule description. | -| `website_ids` | Integer array | Required. A nonempty list of existing storefront website IDs. Website ID `0` is not supported. | -| `customer_group_ids` | Integer array | Required. A nonempty list of existing customer group IDs. | -| `simple_action` | String | Required. One of the four supported discount actions. | -| `discount_amount` | Number | Required. Must be nonnegative. Percentage actions accept values from 0 to 100. | +| `website_ids` | Integer array | Required on create; optional on update. A nonempty list of existing storefront website IDs. Website ID `0` is not supported. | +| `customer_group_ids` | Integer array | Required on create; optional on update. A nonempty list of existing customer group IDs. | +| `simple_action` | String | Required on create; optional on update. One of the four supported discount actions. | +| `discount_amount` | Number | Required on create; optional on update. Must be nonnegative. Percentage actions accept values from 0 to 100. | | `is_active` | Integer | `0` for inactive or `1` for active. Defaults to `0`. | | `stop_rules_processing` | Integer | `1` prevents subsequent rules from applying to matching products. `0` allows further rule processing. Defaults to `1`. | | `sort_order` | Integer | Nonnegative rule priority. Lower values have higher priority. Defaults to `0`. | From bee9d3c41b436ef2b1c13d7de53f709fc59c4f7b Mon Sep 17 00:00:00 2001 From: Sangmi Lee Date: Thu, 1 Oct 2026 12:50:19 +0200 Subject: [PATCH 04/13] docs: simplify catalog price rule guide --- .../catalog-price-rules/index.md | 94 ++++++------------- 1 file changed, 30 insertions(+), 64 deletions(-) diff --git a/src/pages/rest/saas-integrations/catalog-price-rules/index.md b/src/pages/rest/saas-integrations/catalog-price-rules/index.md index 56be9db87..f4e01c2eb 100644 --- a/src/pages/rest/saas-integrations/catalog-price-rules/index.md +++ b/src/pages/rest/saas-integrations/catalog-price-rules/index.md @@ -10,31 +10,18 @@ keywords: # Catalog price rule API -The catalog price rule REST API lets integrations manage product discounts without creating or editing each rule in the Admin. Use the API to discover supported conditions, create rules, and manage existing rules with the same nested condition logic available in the Admin. - -Catalog price rules apply to products for the specified websites and customer groups. For cart price rules, which apply discounts during checkout, use the [salesRules API](https://adobe-commerce-saas.redoc.ly/tag/salesRules/). The two APIs use different condition payloads. +Use these REST endpoints to create, retrieve, update, delete, and search catalog price rules in Adobe Commerce as a Cloud Service. Rules apply product discounts to the specified websites and customer groups and support nested conditions. ## Authentication and scope Authenticate each request with an Adobe Identity Management Service (IMS) access token. The associated Admin role must include the `Magento_CatalogRule::promo_catalog` Access Control List (ACL) resource. Customer and guest access is not supported. -See [REST authentication](../../authentication/index.md) for user and server-to-server authentication. - -Use the Adobe Commerce as a Cloud Service URL structure: - -```http -GET https://.api.commerce.adobe.com//V1/catalogPriceRules/metadata -Authorization: Bearer -Content-Type: application/json -Store: all -``` +See [REST authentication](../../authentication/index.md) for user and server-to-server authentication, and [REST API overview](../../index.md) for the SaaS URL format. -Do not include `/rest` or a store view code in the URL. The `Store` header specifies the request scope, while the rule's `website_ids` specifies the websites where the discount applies. Include `website_ids` when creating a rule. On update, omit it or send `null` to preserve the existing websites. +The `Store` header specifies the request scope. The rule's `website_ids` specifies the websites where the discount applies. Include `website_ids` when creating a rule. On update, omit it or send `null` to preserve the existing websites. ## REST API reference -All six endpoints require the `Magento_CatalogRule::promo_catalog` ACL resource. - | Method | Endpoint | Description | | --- | --- | --- | | `GET` | `/V1/catalogPriceRules/metadata` | Discover discount actions and attributes available for conditions. | @@ -44,7 +31,7 @@ All six endpoints require the `Magento_CatalogRule::promo_catalog` ACL resource. | `PUT` | `/V1/catalogPriceRules/{ruleId}` | Update a rule. | | `DELETE` | `/V1/catalogPriceRules/{ruleId}` | Delete a rule. | -## Discover available conditions +### Discover available conditions Before creating a rule, retrieve the metadata: @@ -52,7 +39,7 @@ Before creating a rule, retrieve the metadata: GET /V1/catalogPriceRules/metadata ``` -The response contains `simple_actions`, the supported discount actions, and `condition_attributes`, the attributes that can be used in rule conditions. Each attribute includes its `attribute_code`, `label`, `input_type`, supported `operators`, and a `value_source` endpoint when values must be retrieved separately. +The response lists supported discount actions and condition attributes, including their operators and value-source endpoints. The following response excerpt shows the category condition: @@ -76,7 +63,7 @@ The following response excerpt shows the category condition: } ``` -The available product attributes depend on the instance's attribute configuration. Use the returned metadata instead of assuming that an attribute or operator is supported. +Available attributes depend on your instance's configuration. Use the attributes and operators returned by the metadata endpoint. Retrieve the IDs needed for rule scope and condition values using the following endpoints. These endpoints have their own permission requirements. @@ -90,7 +77,7 @@ Retrieve the IDs needed for rule scope and condition values using the following Use option values, not option labels, in conditions for select and multiselect attributes. -## Rule fields +### Rule fields Create and update requests wrap the rule fields in a `rule` object. Defaults apply when creating a rule. On update, omitted or null fields preserve their existing values. @@ -110,7 +97,7 @@ Create and update requests wrap the rule fields in a `rule` object. Defaults app | `to_date` | String | Optional end date or UTC datetime. Must not precede `from_date`. | | `condition` | Object | Optional root condition group containing leaf conditions or nested groups. Omitting it on create targets all products within the rule's website and customer group scope. | -### Discount actions +#### Discount actions The `simple_action` determines how `discount_amount` changes the price: @@ -121,7 +108,7 @@ The `simple_action` determines how `discount_amount` changes the price: | `to_percent` | Set the price to the specified percentage. | An amount of `20` produces a price of 20. | | `to_fixed` | Set the price to the specified fixed amount. | An amount of `20` produces a price of 20. | -### Schedule dates +#### Schedule dates Both `from_date` and `to_date` accept: @@ -130,7 +117,7 @@ Both `from_date` and `to_date` accept: Schedule values are stored and returned as UTC datetimes. To clear an existing schedule boundary in an update, send an empty string for that field. Omitting the field or sending `null` preserves its current value. -## Create a rule +### Create a rule The following request creates an inactive rule that reduces prices by 10 percent for products in category `12`, on website `1`, for customer group `1`. Replace these IDs with existing values from your instance. @@ -144,7 +131,6 @@ Request body: { "rule": { "name": "Category discount", - "description": "Ten percent off products in the selected category.", "website_ids": [1], "customer_group_ids": [1], "simple_action": "by_percent", @@ -164,37 +150,26 @@ Request body: } ``` -The response returns the saved rule object, including its generated `rule_id` and default values: +The response returns the saved rule object. This excerpt shows the generated ID and default values: ```json { "rule_id": 42, "name": "Category discount", - "description": "Ten percent off products in the selected category.", "is_active": 0, - "website_ids": [1], - "customer_group_ids": [1], - "simple_action": "by_percent", "discount_amount": 10, "stop_rules_processing": 1, - "sort_order": 0, - "condition": { - "aggregator": "all", - "match": true, - "conditions": [ - { - "attribute_code": "category_ids", - "operator": "==", - "value": "12" - } - ] - } + "sort_order": 0 } ``` Review the saved rule before activating it with `is_active: 1`. You can also set `is_active` explicitly when creating a rule. -## Build nested conditions + + +Rule changes may take time to appear in storefront prices. + +### Build nested conditions The `condition` object is a root group. Each group has a `conditions` array containing leaf conditions, nested groups, or both. @@ -249,7 +224,9 @@ Condition trees support up to 10 levels and 100 total nodes, including the root Use only operators returned for the attribute by the metadata endpoint. Where an operator supports multiple values, supply a comma-separated string, such as `"12,13"`, rather than a JSON array. Scalar operators for structured values require a single value. The `<=>` operator means "is undefined" and does not require a value. -## Retrieve and update a rule +Boolean condition values use `"0"` or `"1"`. Date condition values accept `YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS`. + +### Retrieve and update a rule Retrieve a rule by its ID: @@ -278,6 +255,8 @@ Request body: The response returns the updated rule object. The API validates the combined existing and requested values. For example, updating `discount_amount` to `150` on a `by_percent` rule fails even when `simple_action` is omitted. +Updates that activate a rule or leave it active also validate its existing conditions. If a referenced category, attribute set, or option has been removed, correct the condition before saving. + If you provide `condition`, it replaces the entire condition tree. Omitting `condition` or sending `null` preserves the existing tree. To remove all product conditions, send an empty root group: ```json @@ -294,17 +273,15 @@ If you provide `condition`, it replaces the entire condition tree. Omitting `con An empty root group makes the rule apply to all products within its website and customer group scope. It is not a way to disable the rule. To disable it, set `is_active` to `0`. -## Search rules +### Search rules -Use `/V1/catalogPriceRules/search`, not the collection URL, to list and filter rules. The following request finds active rules, sorts them by ID, and returns the first page of 20 results: +Use `/V1/catalogPriceRules/search` to list and filter rules. The following request returns the first page of 20 active rules: ```text GET /V1/catalogPriceRules/search ?searchCriteria[filterGroups][0][filters][0][field]=is_active &searchCriteria[filterGroups][0][filters][0][value]=1 &searchCriteria[filterGroups][0][filters][0][conditionType]=eq - &searchCriteria[sortOrders][0][field]=rule_id - &searchCriteria[sortOrders][0][direction]=ASC &searchCriteria[pageSize]=20 &searchCriteria[currentPage]=1 ``` @@ -321,7 +298,7 @@ Increase `searchCriteria[currentPage]` to retrieve subsequent pages. An omitted See [Search using REST endpoints](../../use-rest/performing-searches.md) for filter groups, comparison operators, sorting, and pagination. -## Delete a rule +### Delete a rule ```http DELETE /V1/catalogPriceRules/42 @@ -333,20 +310,9 @@ The response confirms deletion: true ``` -## Validation - -Invalid rule input returns HTTP `400`. Validation checks required fields, discount actions and amounts, flags, schedule dates, and the existence of referenced websites and customer groups. - -Conditions are checked against the metadata's allowed attributes and operators. Referenced categories, product attribute sets, and option values must be valid. Boolean condition values use `"0"` or `"1"`, and date condition values accept `YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS`. - -A supplied root `condition` must be an object with a `conditions` array. Malformed nodes, empty nested groups, and condition trees that exceed the depth or node limits are rejected. - -Updates that activate a rule or keep it active also validate its existing conditions. If a referenced category, attribute set, or option has been removed, correct the condition before activating or updating the rule. +## Error handling -Retrieving, updating, or deleting a nonexistent rule returns HTTP `404`. - -## Storefront price updates - -Only active rules within their schedule affect storefront prices for the specified websites and customer groups. - -Price updates are asynchronous. A successful create, update, or delete response confirms the rule operation, not that storefront prices have already changed. Allow time for the price update to complete before checking prices through Catalog Service GraphQL, using the applicable customer group context. +| Status code | Condition | +| --- | --- | +| `400` | Invalid rule input, including malformed conditions or invalid references. | +| `404` | The requested rule does not exist. | From 61f236dc95cdd93de81d9e76deeeb2464a25c79d Mon Sep 17 00:00:00 2001 From: Sangmi Lee Date: Thu, 1 Oct 2026 14:43:46 +0200 Subject: [PATCH 05/13] docs: shorten authentication and website scope guidance --- .../rest/saas-integrations/catalog-price-rules/index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/rest/saas-integrations/catalog-price-rules/index.md b/src/pages/rest/saas-integrations/catalog-price-rules/index.md index f4e01c2eb..f5ba79e93 100644 --- a/src/pages/rest/saas-integrations/catalog-price-rules/index.md +++ b/src/pages/rest/saas-integrations/catalog-price-rules/index.md @@ -12,13 +12,13 @@ keywords: Use these REST endpoints to create, retrieve, update, delete, and search catalog price rules in Adobe Commerce as a Cloud Service. Rules apply product discounts to the specified websites and customer groups and support nested conditions. -## Authentication and scope +## Authentication -Authenticate each request with an Adobe Identity Management Service (IMS) access token. The associated Admin role must include the `Magento_CatalogRule::promo_catalog` Access Control List (ACL) resource. Customer and guest access is not supported. +These endpoints require an [IMS access token](../../authentication/index.md). Your Admin role must include `Magento_CatalogRule::promo_catalog`. -See [REST authentication](../../authentication/index.md) for user and server-to-server authentication, and [REST API overview](../../index.md) for the SaaS URL format. +## Website scope -The `Store` header specifies the request scope. The rule's `website_ids` specifies the websites where the discount applies. Include `website_ids` when creating a rule. On update, omit it or send `null` to preserve the existing websites. +Set the rule's target websites with `website_ids`. The `Store` header controls the REST request scope. See the [REST API overview](../../index.md) for URL and header details. ## REST API reference From 9fff4010f9df3ae9c81edd5f88774a64c85d15b6 Mon Sep 17 00:00:00 2001 From: Sangmi Lee Date: Fri, 2 Oct 2026 17:42:23 +0200 Subject: [PATCH 06/13] docs: apply feedback --- .../catalog-price-rules/index.md | 230 +++++++++--------- 1 file changed, 109 insertions(+), 121 deletions(-) diff --git a/src/pages/rest/saas-integrations/catalog-price-rules/index.md b/src/pages/rest/saas-integrations/catalog-price-rules/index.md index f5ba79e93..4eb040695 100644 --- a/src/pages/rest/saas-integrations/catalog-price-rules/index.md +++ b/src/pages/rest/saas-integrations/catalog-price-rules/index.md @@ -1,5 +1,5 @@ --- -title: Catalog Price Rule REST Endpoints +title: Catalog Price Rules description: Learn how to create, retrieve, update, delete, and search catalog price rules with REST APIs in Adobe Commerce as a Cloud Service. keywords: - REST @@ -8,13 +8,13 @@ keywords: -# Catalog price rule API +# Catalog price rules -Use these REST endpoints to create, retrieve, update, delete, and search catalog price rules in Adobe Commerce as a Cloud Service. Rules apply product discounts to the specified websites and customer groups and support nested conditions. +Use these REST endpoints to manage catalog price rules in Adobe Commerce as a Cloud Service. Rules apply product discounts to the specified websites and customer groups. ## Authentication -These endpoints require an [IMS access token](../../authentication/index.md). Your Admin role must include `Magento_CatalogRule::promo_catalog`. +These endpoints require an [IMS access token](../../authentication/index.md). To access them, you must have the `Magento_CatalogRule::promo_catalog` permission. ## Website scope @@ -31,55 +31,9 @@ Set the rule's target websites with `website_ids`. The `Store` header controls t | `PUT` | `/V1/catalogPriceRules/{ruleId}` | Update a rule. | | `DELETE` | `/V1/catalogPriceRules/{ruleId}` | Delete a rule. | -### Discover available conditions +## Rule fields -Before creating a rule, retrieve the metadata: - -```http -GET /V1/catalogPriceRules/metadata -``` - -The response lists supported discount actions and condition attributes, including their operators and value-source endpoints. - -The following response excerpt shows the category condition: - -```json -{ - "simple_actions": [ - "by_percent", - "by_fixed", - "to_percent", - "to_fixed" - ], - "condition_attributes": [ - { - "attribute_code": "category_ids", - "label": "Category", - "input_type": "select", - "operators": ["==", "!=", "{}", "!{}", "()", "!()"], - "value_source": "/V1/categories" - } - ] -} -``` - -Available attributes depend on your instance's configuration. Use the attributes and operators returned by the metadata endpoint. - -Retrieve the IDs needed for rule scope and condition values using the following endpoints. These endpoints have their own permission requirements. - -| Values | Endpoint | -| --- | --- | -| Website IDs | `GET /V1/store/websites` | -| Customer group IDs | `GET /V1/customerGroups/search` | -| Category IDs | `GET /V1/categories` | -| Product attribute set IDs | `GET /V1/products/attribute-sets/sets/list` | -| Attribute option values | `GET /V1/products/attributes/{attributeCode}/options` | - -Use option values, not option labels, in conditions for select and multiselect attributes. - -### Rule fields - -Create and update requests wrap the rule fields in a `rule` object. Defaults apply when creating a rule. On update, omitted or null fields preserve their existing values. +Create and update requests wrap the rule fields in a `rule` object. Defaults only apply when creating a rule. On update, omitted or null fields preserve their existing values. | Field | Type | Description | | --- | --- | --- | @@ -88,16 +42,16 @@ Create and update requests wrap the rule fields in a `rule` object. Defaults app | `description` | String | Optional rule description. | | `website_ids` | Integer array | Required on create; optional on update. A nonempty list of existing storefront website IDs. Website ID `0` is not supported. | | `customer_group_ids` | Integer array | Required on create; optional on update. A nonempty list of existing customer group IDs. | -| `simple_action` | String | Required on create; optional on update. One of the four supported discount actions. | -| `discount_amount` | Number | Required on create; optional on update. Must be nonnegative. Percentage actions accept values from 0 to 100. | +| `simple_action` | String | Required on create; optional on update. One of the four supported [discount actions](#discount-actions). | +| `discount_amount` | Number | Required on create; optional on update. Percentage actions accept values from 0 to 100. Fixed-amount actions accept values of 0 or greater. | | `is_active` | Integer | `0` for inactive or `1` for active. Defaults to `0`. | | `stop_rules_processing` | Integer | `1` prevents subsequent rules from applying to matching products. `0` allows further rule processing. Defaults to `1`. | | `sort_order` | Integer | Nonnegative rule priority. Lower values have higher priority. Defaults to `0`. | -| `from_date` | String | Optional start date or UTC datetime. | -| `to_date` | String | Optional end date or UTC datetime. Must not precede `from_date`. | -| `condition` | Object | Optional root condition group containing leaf conditions or nested groups. Omitting it on create targets all products within the rule's website and customer group scope. | +| `from_date` | String | Optional start date or UTC datetime. See [Schedule dates](#schedule-dates). | +| `to_date` | String | Optional end date or UTC datetime. Must not precede `from_date`. See [Schedule dates](#schedule-dates). | +| `condition` | Object | Optional root condition group containing leaf conditions or nested groups. Omitting it on create targets all products within the rule's website and customer group scope. See [Conditions](#conditions). | -#### Discount actions +### Discount actions The `simple_action` determines how `discount_amount` changes the price: @@ -108,7 +62,7 @@ The `simple_action` determines how `discount_amount` changes the price: | `to_percent` | Set the price to the specified percentage. | An amount of `20` produces a price of 20. | | `to_fixed` | Set the price to the specified fixed amount. | An amount of `20` produces a price of 20. | -#### Schedule dates +### Schedule dates Both `from_date` and `to_date` accept: @@ -117,71 +71,105 @@ Both `from_date` and `to_date` accept: Schedule values are stored and returned as UTC datetimes. To clear an existing schedule boundary in an update, send an empty string for that field. Omitting the field or sending `null` preserves its current value. -### Create a rule +### Conditions -The following request creates an inactive rule that reduces prices by 10 percent for products in category `12`, on website `1`, for customer group `1`. Replace these IDs with existing values from your instance. +The `condition` object is a root group. Each group has a `conditions` array containing leaf conditions, nested groups, or both. -```http -POST /V1/catalogPriceRules -``` +- `aggregator: "all"` combines children with AND. `"any"` combines them with OR. The default is `"all"`. +- `match` specifies the required result for each child: `true` or `false`. The default is `true`. The `aggregator` determines whether any or all children must have that result. For example, `"all"` with `match: false` requires every child condition to be false. +- A leaf uses `attribute_code`, `operator`, and a string `value`. Do not include `aggregator`, `match`, or child `conditions` on a leaf. +- A nested group uses `aggregator`, `match`, and child `conditions`. Do not include leaf fields on a group. -Request body: +The following `condition` value targets products in attribute set `4` that belong to either category `12` or category `13`. You can send it in a create or update request. ```json { - "rule": { - "name": "Category discount", - "website_ids": [1], - "customer_group_ids": [1], - "simple_action": "by_percent", - "discount_amount": 10, - "condition": { - "aggregator": "all", + "aggregator": "all", + "match": true, + "conditions": [ + { + "attribute_code": "attribute_set_id", + "operator": "==", + "value": "4" + }, + { + "aggregator": "any", "match": true, "conditions": [ { "attribute_code": "category_ids", "operator": "==", "value": "12" + }, + { + "attribute_code": "category_ids", + "operator": "==", + "value": "13" } ] } - } + ] } ``` -The response returns the saved rule object. This excerpt shows the generated ID and default values: +Condition trees support up to 10 levels and 100 total nodes, including the root group. Nested groups must contain at least one child. + +Use only operators returned for the attribute by the [metadata endpoint](#condition-attributes). Where an operator supports multiple values, supply a comma-separated string, such as `"12,13"`, rather than a JSON array. Scalar operators for structured values require a single value. The `<=>` operator means "is undefined" and does not require a value. + +Boolean condition values use `"0"` or `"1"`. Date condition values accept `YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS`. + +### Condition attributes + +The metadata endpoint returns the discount actions and condition attributes available on your instance. Each attribute includes its supported operators and, for attributes with predefined values, the endpoint that returns those values. + +```http +GET /V1/catalogPriceRules/metadata +``` + +The following response is shortened to show only the category condition: ```json { - "rule_id": 42, - "name": "Category discount", - "is_active": 0, - "discount_amount": 10, - "stop_rules_processing": 1, - "sort_order": 0 + "simple_actions": [ + "by_percent", + "by_fixed", + "to_percent", + "to_fixed" + ], + "condition_attributes": [ + { + "attribute_code": "category_ids", + "label": "Category", + "input_type": "select", + "operators": ["==", "!=", "{}", "!{}", "()", "!()"], + "value_source": "/V1/categories" + } + ] } ``` -Review the saved rule before activating it with `is_active: 1`. You can also set `is_active` explicitly when creating a rule. +Available attributes depend on your instance's configuration. Use the attributes and operators returned by the metadata endpoint. - +### Lookup values -Rule changes may take time to appear in storefront prices. +Retrieve the IDs needed for rule scope and condition values using the following endpoints. Each endpoint requires the permission shown next to it. -### Build nested conditions +- Website IDs: `GET /V1/store/websites`, which requires `Magento_Backend::store`. +- Customer group IDs: `GET /V1/customerGroups/search`, which requires `Magento_Customer::group`. +- Category IDs: `GET /V1/categories`, which requires `Magento_Catalog::categories`. +- Product attribute set IDs: `GET /V1/products/attribute-sets/sets/list`, which requires `Magento_Catalog::sets`. +- Attribute option values: `GET /V1/products/attributes/{attributeCode}/options`, which requires `Magento_Catalog::attributes_attributes`. -The `condition` object is a root group. Each group has a `conditions` array containing leaf conditions, nested groups, or both. +Use option values, not option labels, in conditions for select and multiselect attributes. -- `aggregator: "all"` combines children with AND. `"any"` combines them with OR. The default is `"all"`. -- `match` specifies the required result for each child: `true` or `false`. The default is `true`. The `aggregator` determines whether all or any children must have that result. For example, `"all"` with `match: false` requires every child condition to be false. -- A leaf uses `attribute_code`, `operator`, and a string `value`. Do not include `aggregator`, `match`, or child `conditions` on a leaf. -- A nested group uses `aggregator`, `match`, and child `conditions`. Do not include leaf fields on a group. +## Manage rules + +### Create a rule -The following update targets products in attribute set `4` that belong to either category `12` or category `13`. Replace these IDs with valid values from your instance. +The following request creates an inactive rule that reduces prices by 10 percent for products in category `12`, on website `1`, for customer group `1`. ```http -PUT /V1/catalogPriceRules/42 +POST /V1/catalogPriceRules ``` Request body: @@ -189,30 +177,19 @@ Request body: ```json { "rule": { + "name": "Category discount", + "website_ids": [1], + "customer_group_ids": [1], + "simple_action": "by_percent", + "discount_amount": 10, "condition": { "aggregator": "all", "match": true, "conditions": [ { - "attribute_code": "attribute_set_id", + "attribute_code": "category_ids", "operator": "==", - "value": "4" - }, - { - "aggregator": "any", - "match": true, - "conditions": [ - { - "attribute_code": "category_ids", - "operator": "==", - "value": "12" - }, - { - "attribute_code": "category_ids", - "operator": "==", - "value": "13" - } - ] + "value": "12" } ] } @@ -220,15 +197,26 @@ Request body: } ``` -Condition trees support up to 10 levels and 100 total nodes, including the root group. Nested groups must contain at least one child. +The response returns the saved rule object. The following response is shortened to show the generated ID and default values: + +```json +{ + "rule_id": 42, + "name": "Category discount", + "is_active": 0, + "discount_amount": 10, + "stop_rules_processing": 1, + "sort_order": 0 +} +``` -Use only operators returned for the attribute by the metadata endpoint. Where an operator supports multiple values, supply a comma-separated string, such as `"12,13"`, rather than a JSON array. Scalar operators for structured values require a single value. The `<=>` operator means "is undefined" and does not require a value. +Review the saved rule before activating it with `is_active: 1`. -Boolean condition values use `"0"` or `"1"`. Date condition values accept `YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS`. + -### Retrieve and update a rule +Rule changes may take time to affect storefront prices. -Retrieve a rule by its ID: +### Retrieve a rule ```http GET /V1/catalogPriceRules/42 @@ -236,6 +224,8 @@ GET /V1/catalogPriceRules/42 The response returns the rule object, including its full condition tree. +### Update a rule + Updates preserve existing values for fields you omit or set to `null`. For example, the following request activates the rule and changes the discount to 15 percent without changing its name, scope, schedule, or conditions: ```http @@ -290,13 +280,11 @@ The query parameters are shown on separate lines for readability. Send them as o The response contains: -- `items`, an array of rule objects, including their condition trees. -- `search_criteria`, the criteria used for the request. -- `total_count`, the total number of matching rules across all pages. - -Increase `searchCriteria[currentPage]` to retrieve subsequent pages. An omitted or zero page size defaults to 200. Values above 500 are capped at 500. Negative page sizes and current pages below 1 are rejected. +- `items` - an array of rule objects, including their condition trees. +- `search_criteria` - the criteria used for the request. +- `total_count` - the total number of matching rules across all pages. -See [Search using REST endpoints](../../use-rest/performing-searches.md) for filter groups, comparison operators, sorting, and pagination. +See [Search using REST endpoints](../../use-rest/performing-searches.md) for filter groups, comparison operators, sorting, and pagination. This endpoint returns 200 rules per page by default and at most 500. ### Delete a rule From cb43b4705db7e173da606112b37eb35e7c183179 Mon Sep 17 00:00:00 2001 From: Jared Hoover Date: Fri, 2 Oct 2026 17:53:58 -0500 Subject: [PATCH 07/13] CCSAAS-5490 initiate upload recaptcha GraphQL --- .../schema/uploads/mutations/initiate-upload.md | 14 ++++++++++++++ src/pages/graphql/usage/protected-mutations.md | 1 + 2 files changed, 15 insertions(+) diff --git a/src/pages/graphql/schema/uploads/mutations/initiate-upload.md b/src/pages/graphql/schema/uploads/mutations/initiate-upload.md index 57fc6b7f7..f162729d4 100644 --- a/src/pages/graphql/schema/uploads/mutations/initiate-upload.md +++ b/src/pages/graphql/schema/uploads/mutations/initiate-upload.md @@ -28,6 +28,20 @@ Use the `upload_url` from the response to PUT the file directly to S3. See [Uplo After the file is successfully uploaded, use the [`finishUpload` mutation](finish-upload.md) to complete the upload process. +## reCAPTCHA validation + +Guest shoppers can call the `initiateUpload` mutation without a customer token, for example, to attach an image to a guest return. To limit automated upload requests, merchants can require Google reCAPTCHA validation on this mutation. This setting is disabled by default. To enable it, set [**Enable for Presigned Upload**](https://experienceleague.adobe.com/en/docs/commerce-admin/config/security/google-recaptcha-storefront) in **Stores** > **Configuration** > **Security** > **Google reCAPTCHA Storefront** > **Storefront**. + +When the setting is enabled, each `initiateUpload` request must include a valid reCAPTCHA token in the `X-ReCaptcha` HTTP header: + +```text +X-ReCaptcha: +``` + +If the token is missing or invalid, the request fails and Commerce does not issue a presigned URL. The `finishUpload` mutation does not require a reCAPTCHA token because it can only complete an upload that `initiateUpload` started. + +For more information about reCAPTCHA headers, see [Protected mutations](../../../usage/protected-mutations.md). + ## Syntax ```graphql diff --git a/src/pages/graphql/usage/protected-mutations.md b/src/pages/graphql/usage/protected-mutations.md index 1ba93e7a7..cc33d7997 100644 --- a/src/pages/graphql/usage/protected-mutations.md +++ b/src/pages/graphql/usage/protected-mutations.md @@ -57,6 +57,7 @@ The following table lists the forms and mutations that can be configured to requ | Enable for Checkout/Placing Order | `setPaymentMethodOnCart`, `setPaymentMethodAndPlaceOrder` | | Enable for Coupon Codes | `applyCouponToCart` | | Enable for Resend Confirmation Email | `resendConfirmationEmail` | +| Enable for Presigned Upload | [SaaS only](https://experienceleague.adobe.com/en/docs/commerce/user-guides/product-solutions) `initiateUpload` | ## Related topics From 4a894fc3e7645ee61f003de64dee57e94d880874 Mon Sep 17 00:00:00 2001 From: Jared Hoover Date: Mon, 5 Oct 2026 17:09:19 -0500 Subject: [PATCH 08/13] ACCS-1156 Custom Shipping Discounts --- src/pages/config.md | 1 + src/pages/rest/saas-integrations/index.md | 1 + .../shipping-discounts/index.md | 135 ++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 src/pages/rest/saas-integrations/shipping-discounts/index.md diff --git a/src/pages/config.md b/src/pages/config.md index 80b6409c9..5994c1c93 100644 --- a/src/pages/config.md +++ b/src/pages/config.md @@ -161,6 +161,7 @@ - [Login as Customer](/rest/saas-integrations/login-as-customer/index.md) - [Order management](/rest/saas-integrations/order-management/index.md) - [S3 uploads](/rest/saas-integrations/s3-uploads/index.md) + - [Shipping discounts](/rest/saas-integrations/shipping-discounts/index.md) - [System configuration](/rest/saas-integrations/system-config/index.md) - [Introduction](/graphql/index.md) - [Usage](/graphql/usage/index.md) diff --git a/src/pages/rest/saas-integrations/index.md b/src/pages/rest/saas-integrations/index.md index 5e6151745..4fc31bff6 100644 --- a/src/pages/rest/saas-integrations/index.md +++ b/src/pages/rest/saas-integrations/index.md @@ -14,4 +14,5 @@ Review the following topics to learn more about REST APIs available only on Adob - [Login as Customer](login-as-customer/index.md) - [Order management](order-management/index.md) - [S3 uploads](s3-uploads/index.md) +- [Shipping discounts](shipping-discounts/index.md) - [System configuration](system-config/index.md) diff --git a/src/pages/rest/saas-integrations/shipping-discounts/index.md b/src/pages/rest/saas-integrations/shipping-discounts/index.md new file mode 100644 index 000000000..74d7230d7 --- /dev/null +++ b/src/pages/rest/saas-integrations/shipping-discounts/index.md @@ -0,0 +1,135 @@ +--- +title: Custom Shipping Discounts +description: Learn how to apply, retrieve, and remove a custom shipping discount on a cart with the admin REST API in Adobe Commerce as a Cloud Service. +keywords: + - REST + - Integration +--- + + + +# Custom shipping discounts + +The custom shipping discounts let an administrator or integration apply an arbitrary discount to the shipping amount of a specific cart. Use it for cases that do not fit a cart price rule, such as a goodwill credited to an individual shopper. + +If a discount fits a rule-based pattern, use a [cart price rule](https://experienceleague.adobe.com/en/docs/commerce-admin/marketing/promotions/cart-rules/price-rules-cart) instead. + +## Endpoints + +| Method | Endpoint | Description | +| --- | --- | --- | +| `GET` | `/V1/carts/:cartId/shipping-discount` | Retrieve the active custom shipping discount on the cart. | +| `POST` | `/V1/carts/:cartId/shipping-discount` | Apply a custom shipping discount to the cart, or replace the existing one. | +| `DELETE` | `/V1/carts/:cartId/shipping-discount` | Remove the custom shipping discount from the cart. | + +All three endpoints require an admin or integration token with the `Magento_ShippingDiscountApi::manage` role. + +The `:cartId` value must identify an active cart. If the cart does not exist or has already been converted to an order, the endpoints return HTTP status `404`. + +## Apply a shipping discount + +The `POST /V1/carts/:cartId/shipping-discount` endpoint applies a custom shipping discount to the cart and immediately recalculates the cart totals. + +The request body contains the following fields: + +| Field | Type | Description | +| --- | --- | --- | +| `amount` | Float | Required. The discount amount, in the cart's currency, not the store's base currency. The value must be greater than zero, both as submitted and after Commerce converts it to the base currency with the cart's exchange rate. | +| `reason` | String | Required. The reason for the discount, such as `goodwill credit`. Maximum 255 characters. The reason is stored for auditing and is not shown to the customer. | + +A cart can have only one custom shipping discount. If the cart already has one, a new `POST` request replaces the existing amount and reason instead of adding a second discount. + +### Example: apply a shipping discount + +Request: + +```bash +curl -X POST "https:///rest/V1/carts/42/shipping-discount" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer " \ + -d '{"amount": 2.00, "reason": "goodwill credit"}' +``` + +A successful request returns HTTP status `200`. + +## Retrieve a shipping discount + +The `GET /V1/carts/:cartId/shipping-discount` endpoint returns the active custom shipping discount on the cart. + +### Example: retrieve a shipping discount + +Request: + +```bash +curl -X GET "https:///rest/V1/carts/42/shipping-discount" \ + -H "Authorization: Bearer " +``` + +Response: + +```json +{ + "cart_id": 42, + "amount": 2.00, + "base_amount": 2.00, + "applied_amount": 2.00, + "base_applied_amount": 2.00, + "reason": "goodwill credit", + "actor_user_id": 7, + "actor_user_type": 2 +} +``` + +The response contains the following fields: + +| Field | Description | +| --- | --- | +| `cart_id` | The ID of the cart that the discount applies to. | +| `amount` | The requested discount amount, in the cart's currency. | +| `base_amount` | The requested discount amount, converted to the store's base currency. | +| `applied_amount` | The amount actually deducted from shipping during the last totals calculation, in the cart's currency. See [How Commerce applies the discount](#how-commerce-applies-the-discount). | +| `base_applied_amount` | The amount actually deducted from shipping during the last totals calculation, in the store's base currency. | +| `reason` | The reason supplied when the discount was applied. | +| `actor_user_id` | The ID of the admin user or integration that applied the discount. Interpret this value together with `actor_user_type`, because admin user IDs and integration IDs can overlap. | +| `actor_user_type` | The type of caller that applied the discount: `1` for an integration or `2` for an admin user. | + +If the cart has no active custom shipping discount, the endpoint returns HTTP status `404`. + +## Remove a shipping discount + +The `DELETE /V1/carts/:cartId/shipping-discount` endpoint removes the custom shipping discount from the cart and recalculates the cart totals. If the cart has no discount, the request still succeeds. + +### Example: remove a shipping discount + +Request: + +```bash +curl -X DELETE "https:///rest/V1/carts/42/shipping-discount" \ + -H "Authorization: Bearer " +``` + +A successful request returns HTTP status `200`. + +## How Commerce applies the discount + +Commerce reapplies the custom shipping discount each time it recalculates the cart totals, so the discount persists when the cart changes. The following rules apply: + +- The applied amount never exceeds the remaining shipping amount. If a cart price rule already discounts shipping, the custom discount applies only to the shipping amount that remains. In this case, `applied_amount` is less than `amount`. +- The cart totals (`GET /V1/carts/:cartId/totals`) include a separate **Shipping Discount** total segment with the `admin_shipping_discount` code. +- The discount is added to the cart and order discount amount, with the **Shipping Discount** label in the discount description. The **Shipping & Handling** amount continues to show the full shipping amount before the discount. + +## Apply a shipping discount during an order edit + +You can apply a custom shipping discount when you [edit an order](../order-management/index.md#edit-orders). Call `POST /V1/carts/:cartId/shipping-discount` with the cart ID that `POST /V1/orders/{orderId}/edit/start` returns, then submit the edit. If order edit history comments are enabled, Commerce adds a comment to the replacement order when the edit applies, changes, or removes a custom shipping discount. The comment shows the applied amount, which can be less than the requested amount. + +When you edit an order that already has a custom shipping discount, Commerce carries the discount forward to the new edit cart. The discount remains on the order across subsequent edits until you remove it with `DELETE /V1/carts/:cartId/shipping-discount`. + +## Errors + +| HTTP status | Cause | +| --- | --- | +| `400` | The `amount` is zero, negative, too large, or too small to store, either as submitted or after conversion to the base currency, or the `reason` exceeds 255 characters. | +| `400` | The cart has more than one shipping address. | +| `400` | Another request is modifying the shipping discount on the cart, or the cart is being placed as an order. | +| `401` | The request does not include an admin or integration token, the token is a customer or guest token, or the token does not have the `Magento_ShippingDiscountApi::manage` role resource. | +| `404` | The cart does not exist or is no longer active, or (for `GET`) the cart has no active custom shipping discount. | From 7b4d974b31eaa4fccfabc46646be16e4d761a6e9 Mon Sep 17 00:00:00 2001 From: Jared Hoover Date: Tue, 6 Oct 2026 15:14:32 -0500 Subject: [PATCH 09/13] ACCS-1155 custom price rest --- src/pages/config.md | 1 + .../cart-custom-price/index.md | 143 ++++++++++++++++++ src/pages/rest/saas-integrations/index.md | 1 + 3 files changed, 145 insertions(+) create mode 100644 src/pages/rest/saas-integrations/cart-custom-price/index.md diff --git a/src/pages/config.md b/src/pages/config.md index 80b6409c9..4ed2ec9b6 100644 --- a/src/pages/config.md +++ b/src/pages/config.md @@ -156,6 +156,7 @@ - [Multicoupon](/rest/modules/multicoupon/index.md) - [Sales refunds](/rest/modules/sales/index.md) - [SaaS integrations](/rest/saas-integrations/index.md) + - [Cart item custom price](/rest/saas-integrations/cart-custom-price/index.md) - [Custom email](/rest/saas-integrations/custom-email/index.md) - [Gift card accounts](/rest/saas-integrations/gift-card-accounts/index.md) - [Login as Customer](/rest/saas-integrations/login-as-customer/index.md) diff --git a/src/pages/rest/saas-integrations/cart-custom-price/index.md b/src/pages/rest/saas-integrations/cart-custom-price/index.md new file mode 100644 index 000000000..feb80919a --- /dev/null +++ b/src/pages/rest/saas-integrations/cart-custom-price/index.md @@ -0,0 +1,143 @@ +--- +title: Cart Item Custom Price +description: Learn how to set a custom price on a cart item with the custom_price extension attribute on the add and update cart item REST endpoints. +keywords: + - REST + - Integration +--- + + + +# Cart item custom price + +The cart item custom price capability lets admins and integrations override the catalog price of an item in Adobe Commerce as a Cloud Service by using the `custom_price` extension attribute with the add and update cart item REST endpoints. + +These endpoints are designed for: + +* Integrations that set prices calculated outside of Commerce +* Admin workflows, such as editing an order, that change the price of a cart line + +## Authentication + +All requests that include `custom_price` require an admin or integration [bearer token](../../authentication/index.md). Requests that include `custom_price` with a customer token are rejected. Guest cart endpoints are not available in Adobe Commerce as a Cloud Service. + +## Limitations + +* **Bundle products with dynamic pricing** — Not supported, because the price is calculated from the prices of their child products. Bundle products with fixed pricing are supported. +* **B2B negotiable quotes** — Not supported. Use the negotiable quote API to set negotiated prices. + +## REST API reference + +| Method | URL | Description | +|--------|-----|-------------| +| POST | `/V1/carts/:cartId/items` | Add an item to the cart at a custom price | +| PUT | `/V1/carts/:cartId/items/:itemId` | Change the price of an item that is already in the cart | +| GET | `/V1/carts/:cartId/items` | Retrieve cart items, including `custom_price` | +| GET | `/V1/carts/:cartId` | Retrieve the cart, including `custom_price` on each item | + +### Field reference + +The `custom_price` attribute is part of the `extension_attributes` object of the `cartItem` payload. + +| Field | Type | Valid values | Required | +|---|---|---|---| +| `extension_attributes.custom_price` | float | >= 0. A value of `0` adds the item for free. | Optional. When omitted, the item uses the catalog price. | +| `item_id` | int | ID of an existing cart line | Required to change the price of an item that is already in the cart | + +### Add an item at a custom price + +Adds a new item to the cart and applies the custom price to each unit. + +| Item | Value | +|---|---| +| **Method** | `POST` | +| **URL** | `/V1/carts/:cartId/items` | + +**Request body:** + +```json +{ + "cartItem": { + "sku": "t-shirt", + "qty": 1, + "quote_id": 17, + "extension_attributes": { + "custom_price": 15.00 + } + } +} +``` + +**Response (200):** + +Returns the cart item with the applied price in `extension_attributes.custom_price`. + +### Change the price of an existing cart item + +Changes the price of a cart line. Use `GET /V1/carts/:cartId/items` to find the `item_id` of the line. + +| Item | Value | +|---|---| +| **Method** | `PUT` | +| **URL** | `/V1/carts/:cartId/items/:itemId` | + +**Request body:** + +```json +{ + "cartItem": { + "item_id": 8, + "sku": "hat", + "qty": 1, + "quote_id": 17, + "extension_attributes": { + "custom_price": 5.00 + } + } +} +``` + +**Response (200):** + +Returns the cart item with the applied price in `extension_attributes.custom_price`. + + + +If you add a product that is already in the cart with a `custom_price` but without an `item_id`, the request is rejected and the existing cart line is left unchanged. To add units to an existing line at a new price, send its `item_id` with the new total quantity. + +### Retrieve custom prices + +| Item | Value | +|---|---| +| **Method** | `GET` | +| **URL** | `/V1/carts/:cartId/items` or `/V1/carts/:cartId` | + +**Response (200):** + +Each cart item that has a custom price includes it in the `extension_attributes` object. + +```json +{ + "item_id": 8, + "sku": "hat", + "qty": 1, + "extension_attributes": { + "custom_price": 5 + } +} +``` + +## Error handling + +If a request fails for any reason, the cart is left exactly as it was before the request. A successful response always means that the custom price was applied. + +| Condition | Error message | +|---|---| +| The token is not an admin or integration token | `Setting a custom price is not permitted for this account.` | +| The price is negative or not a finite number | `custom_price must be a finite, non-negative number.` | +| The product is a bundle with dynamic pricing | `custom_price is not supported for this product type.` | +| The cart is a B2B negotiable quote | `custom_price is not supported on a negotiable quote. Use the negotiable quote API to set negotiated prices.` | +| The product is already in the cart and no `item_id` is supplied | `This product is already on the cart. To change the price of that line, supply its item_id; ...` | +| The price could not be applied after the item was saved, for example because the product was deleted or disabled during the request | `The item could not be added with the requested custom price.` | + +Non-numeric `custom_price` values, such as `"not-a-price"`, are rejected by REST type validation with a `400` response before the price check runs. diff --git a/src/pages/rest/saas-integrations/index.md b/src/pages/rest/saas-integrations/index.md index 5e6151745..ef67b9892 100644 --- a/src/pages/rest/saas-integrations/index.md +++ b/src/pages/rest/saas-integrations/index.md @@ -9,6 +9,7 @@ keywords: Review the following topics to learn more about REST APIs available only on Adobe Commerce as a Cloud Service: +- [Cart item custom price](cart-custom-price/index.md) - [Custom email](custom-email/index.md) - [Gift card accounts](gift-card-accounts/index.md) - [Login as Customer](login-as-customer/index.md) From 073ecc970210938d875d6b24f6d7ec733fc1ed99 Mon Sep 17 00:00:00 2001 From: Jared Hoover Date: Tue, 6 Oct 2026 15:32:21 -0500 Subject: [PATCH 10/13] review --- src/pages/rest/use-rest/bulk-endpoints.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/rest/use-rest/bulk-endpoints.md b/src/pages/rest/use-rest/bulk-endpoints.md index c17e494bd..fce04fde7 100644 --- a/src/pages/rest/use-rest/bulk-endpoints.md +++ b/src/pages/rest/use-rest/bulk-endpoints.md @@ -11,13 +11,13 @@ Bulk API endpoints differ from other REST endpoints in that they combine multipl -In Adobe Commerce as a Cloud Service, message queues run automatically. There is no need to manage queues or install a message broker. + - +In Adobe Commerce as a Cloud Service, Bulk API limits are determined by the **Maximum Entities per Bulk Request** setting in the [Store Configuration](https://experienceleague.adobe.com/en/docs/commerce-admin/config/general/bulk-api). - +Message queues also run automatically. There is no need to manage queues or install a message broker. -In Adobe Commerce as a Cloud Service, Bulk API limits are determined by the **Maximum Entities per Bulk Request** setting in the [Store Configuration](https://experienceleague.adobe.com/en/docs/commerce-admin/config/general/bulk-api). + [PaaS only](https://experienceleague.adobe.com/en/docs/commerce/user-guides/product-solutions) Cron jobs are the default mechanism for [managing message queues](https://experienceleague.adobe.com/en/docs/commerce-operations/configuration-guide/message-queues/manage-message-queues) and starting message queue [consumers](https://experienceleague.adobe.com/en/docs/commerce-operations/configuration-guide/message-queues/consumers), but you can also use external process control systems (like [Supervisor](https://supervisord.readthedocs.io/en/latest/)) to monitor process management. You can use the [`bin/magento queue:consumers:start async.operations.all`](https://experienceleague.adobe.com/en/docs/commerce-operations/configuration-guide/cli/start-message-queues) command to manually start the `async.operations.all` consumer that handles asynchronous and bulk API messages. However, manually starting consumers is not recommended because it requires you to keep your terminal session connected. From 34d560205dd81ee514118944cb3c9a1c92f2a6ee Mon Sep 17 00:00:00 2001 From: Jared Hoover Date: Tue, 6 Oct 2026 15:46:24 -0500 Subject: [PATCH 11/13] review --- .../shipping-discounts/index.md | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/pages/rest/saas-integrations/shipping-discounts/index.md b/src/pages/rest/saas-integrations/shipping-discounts/index.md index 74d7230d7..f23d82cfa 100644 --- a/src/pages/rest/saas-integrations/shipping-discounts/index.md +++ b/src/pages/rest/saas-integrations/shipping-discounts/index.md @@ -14,6 +14,20 @@ The custom shipping discounts let an administrator or integration apply an arbit If a discount fits a rule-based pattern, use a [cart price rule](https://experienceleague.adobe.com/en/docs/commerce-admin/marketing/promotions/cart-rules/price-rules-cart) instead. +## How Commerce applies the discount + +Commerce reapplies the custom shipping discount each time it recalculates the cart totals, so the discount persists when the cart changes. The following rules apply: + +- The applied amount never exceeds the remaining shipping amount. If a cart price rule already discounts shipping, the custom discount applies only to the shipping amount that remains. In this case, `applied_amount` is less than `amount`. +- The cart totals (`GET /V1/carts/:cartId/totals`) include a separate **Shipping Discount** total segment with the `admin_shipping_discount` code. +- The discount is added to the cart and order discount amount, with the **Shipping Discount** label in the discount description. The **Shipping & Handling** amount continues to show the full shipping amount before the discount. + +## Apply a shipping discount during an order edit + +You can apply a custom shipping discount when you [edit an order](../order-management/index.md#edit-orders). Call `POST /V1/carts/:cartId/shipping-discount` with the cart ID that `POST /V1/orders/{orderId}/edit/start` returns, then submit the edit. If order edit history comments are enabled, Commerce adds a comment to the replacement order when the edit applies, changes, or removes a custom shipping discount. The comment shows the applied amount, which can be less than the requested amount. + +When you edit an order that already has a custom shipping discount, Commerce carries the discount forward to the new edit cart. The discount remains on the order across subsequent edits until you remove it with `DELETE /V1/carts/:cartId/shipping-discount`. + ## Endpoints | Method | Endpoint | Description | @@ -110,20 +124,6 @@ curl -X DELETE "https:///rest/V1/carts/42/shipping-discount" \ A successful request returns HTTP status `200`. -## How Commerce applies the discount - -Commerce reapplies the custom shipping discount each time it recalculates the cart totals, so the discount persists when the cart changes. The following rules apply: - -- The applied amount never exceeds the remaining shipping amount. If a cart price rule already discounts shipping, the custom discount applies only to the shipping amount that remains. In this case, `applied_amount` is less than `amount`. -- The cart totals (`GET /V1/carts/:cartId/totals`) include a separate **Shipping Discount** total segment with the `admin_shipping_discount` code. -- The discount is added to the cart and order discount amount, with the **Shipping Discount** label in the discount description. The **Shipping & Handling** amount continues to show the full shipping amount before the discount. - -## Apply a shipping discount during an order edit - -You can apply a custom shipping discount when you [edit an order](../order-management/index.md#edit-orders). Call `POST /V1/carts/:cartId/shipping-discount` with the cart ID that `POST /V1/orders/{orderId}/edit/start` returns, then submit the edit. If order edit history comments are enabled, Commerce adds a comment to the replacement order when the edit applies, changes, or removes a custom shipping discount. The comment shows the applied amount, which can be less than the requested amount. - -When you edit an order that already has a custom shipping discount, Commerce carries the discount forward to the new edit cart. The discount remains on the order across subsequent edits until you remove it with `DELETE /V1/carts/:cartId/shipping-discount`. - ## Errors | HTTP status | Cause | From 818e375a43ce878dec2145d4f74ac71dcc697ed9 Mon Sep 17 00:00:00 2001 From: Jared Hoover Date: Tue, 6 Oct 2026 16:06:50 -0500 Subject: [PATCH 12/13] review --- src/pages/graphql/schema/uploads/mutations/initiate-upload.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/graphql/schema/uploads/mutations/initiate-upload.md b/src/pages/graphql/schema/uploads/mutations/initiate-upload.md index f162729d4..b90fae0c7 100644 --- a/src/pages/graphql/schema/uploads/mutations/initiate-upload.md +++ b/src/pages/graphql/schema/uploads/mutations/initiate-upload.md @@ -30,7 +30,9 @@ After the file is successfully uploaded, use the [`finishUpload` mutation](finis ## reCAPTCHA validation -Guest shoppers can call the `initiateUpload` mutation without a customer token, for example, to attach an image to a guest return. To limit automated upload requests, merchants can require Google reCAPTCHA validation on this mutation. This setting is disabled by default. To enable it, set [**Enable for Presigned Upload**](https://experienceleague.adobe.com/en/docs/commerce-admin/config/security/google-recaptcha-storefront) in **Stores** > **Configuration** > **Security** > **Google reCAPTCHA Storefront** > **Storefront**. +You can call the `initiateUpload` for many reasons, for example to attach an image to a return. To limit automated upload requests, merchants can require Google reCAPTCHA validation on this mutation. This setting is disabled by default. To enable it, set [**Enable for Presigned Upload**](https://experienceleague.adobe.com/en/docs/commerce-admin/config/security/google-recaptcha-storefront) in **Stores** > **Configuration** > **Security** > **Google reCAPTCHA Storefront** > **Storefront**. + +When working with guest shoppers, call the `initiateUpload` mutation without a customer token. When the setting is enabled, each `initiateUpload` request must include a valid reCAPTCHA token in the `X-ReCaptcha` HTTP header: From d9cea5a6664170a19c8ed426ace51ad7b7580787 Mon Sep 17 00:00:00 2001 From: Jared Hoover Date: Wed, 7 Oct 2026 14:04:41 -0500 Subject: [PATCH 13/13] review --- .../cart-custom-price/index.md | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/src/pages/rest/saas-integrations/cart-custom-price/index.md b/src/pages/rest/saas-integrations/cart-custom-price/index.md index feb80919a..db5ea846b 100644 --- a/src/pages/rest/saas-integrations/cart-custom-price/index.md +++ b/src/pages/rest/saas-integrations/cart-custom-price/index.md @@ -24,7 +24,7 @@ All requests that include `custom_price` require an admin or integration [bearer ## Limitations * **Bundle products with dynamic pricing** — Not supported, because the price is calculated from the prices of their child products. Bundle products with fixed pricing are supported. -* **B2B negotiable quotes** — Not supported. Use the negotiable quote API to set negotiated prices. +* **B2B negotiable quotes** — Not supported. Use [`PUT /V1/negotiableQuote/:quoteId`](../../b2b/negotiable-update.md) to set negotiated prices. ## REST API reference @@ -46,12 +46,7 @@ The `custom_price` attribute is part of the `extension_attributes` object of the ### Add an item at a custom price -Adds a new item to the cart and applies the custom price to each unit. - -| Item | Value | -|---|---| -| **Method** | `POST` | -| **URL** | `/V1/carts/:cartId/items` | +`POST /V1/carts/:cartId/items` adds a new item to the cart and applies the custom price to each unit. **Request body:** @@ -74,12 +69,11 @@ Returns the cart item with the applied price in `extension_attributes.custom_pri ### Change the price of an existing cart item -Changes the price of a cart line. Use `GET /V1/carts/:cartId/items` to find the `item_id` of the line. +`PUT /V1/carts/:cartId/items/:itemId` changes the price of a cart line. Use `GET /V1/carts/:cartId/items` to find the `item_id` of the line. -| Item | Value | -|---|---| -| **Method** | `PUT` | -| **URL** | `/V1/carts/:cartId/items/:itemId` | + + +If you add a product that is already in the cart with a `custom_price` but without an `item_id`, the request is rejected and the existing cart line is left unchanged. To add units to an existing line at a new price, send its `item_id` with the new total quantity. **Request body:** @@ -101,16 +95,9 @@ Changes the price of a cart line. Use `GET /V1/carts/:cartId/items` to find the Returns the cart item with the applied price in `extension_attributes.custom_price`. - - -If you add a product that is already in the cart with a `custom_price` but without an `item_id`, the request is rejected and the existing cart line is left unchanged. To add units to an existing line at a new price, send its `item_id` with the new total quantity. - ### Retrieve custom prices -| Item | Value | -|---|---| -| **Method** | `GET` | -| **URL** | `/V1/carts/:cartId/items` or `/V1/carts/:cartId` | +`GET /V1/carts/:cartId/items` and `GET /V1/carts/:cartId` return the `custom_price` for each cart item. **Response (200):**