Marketplaces / WooCommerce Technical Sccope / WooCommerce Product Management / WooCommerce Update Product

WooCommerce Update Product

Version Date Created / Updated Notes
v1.0 Hristiyan Georgiev Initial version

Much like the rest of the flows, we need to separate the updates to product update an variant update. We want to use the product update when updating a simple product, and the variant update when updating a product that is part of a variation.

Protect flags handle :

The good thing is that we do not need to block any communication but rather we can exclude fields that we don’t want to update.

  • Protect the whole item - This field blocks all content updates for the relevant product to the marketplace except for Stock & Price update. WooCommerce allows us to only update the stock & price in a single “full” update call, but it will be misleading for the end user and if we have this flag, we want to skip the update and leave the List/Update the whole item on Pending.
  • Protect Price - This means we include everything except the price related fields in our payload - regular_price, sale_price, date_on_sale_from_gmt and date_on_sale_to_gmt
  • Protect Stock - This means we include everything except stock_quantity field in our payload.

Product Update

API Call: POST /wp-json/wc/v3/products/batch API Docs: https://woocommerce.github.io/woocommerce-rest-api-docs/?shell#batch-update-products

We want to use this call when updating a simple product (not a variation). We also need to perform this call for the parent product update as part of our variation call. Since we cannot identify when the user wants to update the parent and when the parent’s variant, we will always perform both calls for the parent and it’s variant.

All triggers, validations except the protect flags mentioned above, are as per the listing abstraction for product update - Product Listing general requirements.

Since we will be updating the parent with this call, we need to add an additional trigger - Listing WooCommerce > Is Parent = Yes . This trigger should be on an OR basis meaning that we pick a product that is NOT part of a variation OR if part of a variation, it needs to be the parent. In all other cases we should use the Variant update call.

Please note that we can include up to 100 objects in the body.

Example call :

{
    "update": [
        {
            "id": 130,
            "name": "Trash",
            "description": "Its no good",
            "short_description": "The almsot best product",
            "sku": "STAMAT14213135",
            "global_unique_id": "",
            "regular_price": "1123",
            "sale_price": "999",
            "date_on_sale_from_gmt": null,
            "date_on_sale_to_gmt": null,
            "manage_stock": true,
            "stock_quantity": 22,
            "sold_individually": "true",
            "weight": "123",
            "dimensions": {
                "length": "22",
                "width": "12",
                "height": "5"
            },
            "categories": [
                {
                    "id": 15
                }
            ],
            "tags": [
                {
                    "name": "blablaBLA"
                }
            ],
            "images": [
                {
                    "src": "https://cdn.pixabay.com/photo/2017/08/09/04/53/texture-2613518_1280.jpg"
                }
            ],
            "attributes": [
                {
                    "id": 1,
                    "variation": true,
                    "visible": true,
                    "options": [
                        "S",
                        "M"
                    ]
                }
            ],
            "brands": [
                {
                    "id": 24
                }
            ]
        },
        {
            "id": 128,
            "name": "TestTest",
            "description": "ITestTest",
            "short_description": "The test product ever",
            "sku": "STA4533744",
            "global_unique_id": "",
            "regular_price": "100",
            "sale_price": "59",
            "date_on_sale_from_gmt": null,
            "date_on_sale_to_gmt": null,
            "manage_stock": true,
            "stock_quantity": 22,
            "sold_individually": "true",
            "weight": "123",
            "dimensions": {
                "length": "22",
                "width": "12",
                "height": "5"
            },
            "categories": [
                {
                    "id": 15
                }
            ],
            "tags": [
                {
                    "name": "UnoDosTres"
                }
            ],
            "images": [
                {
                    "src": "https://cdn.pixabay.com/photo/2017/08/09/04/53/texture-2613518_1280.jpg"
                }
            ],
            "attributes": [
                {
                    "id": 1,
                    "variation": true,
                    "visible": true,
                    "options": [
                        "S",
                        "M"
                    ]
                }
            ],
            "brands": [
                {
                    "id": 24
                }
            ]
        }
    ]
}

Mapping :

WooCommerce Field MCPro Field Notes
id Listing > Channel Item ID
name Listing > Title
description Listing > Description Description is html friendly.
The design template logic remains so if we have design template we pick as per abstraction.
short_description Listing WooCommerce > Short Description
sku Product > SKU
global_unique_id Product Account >

Marketplace EAN OR Product > EAN OR Product > Barcode OR Product > MPN OR Product >UPC | Product Account is with priority. The priority in the other fields is : EAN, Barcode, MPN, UPC | | regular_price | | Listing > RRP | We need to have a logic and if RRP is empty, we send Price as regular_price and sale_price as empty. Please note that we exclude this field when updating a variation parent. | | sale_price | | Listing > Price | Please note that it looks like Woo does not have any validations for prices whatsoever, so it is possible that we send sale_price higher than regular_price. They will still return success response but the higher sale price will not show, only the lower price will show. We want to have internal validation and if we have Price> RRP, we want to store internal error. Please note that we exclude this field when updating a variation parent. | | date_on_sale_from_gmt | | Listing WooCommerce > Promotion Date Start | We need to convert it into an ISO8601 format. We only need to send if both fields are filled. If one or both are not filled, we exclude them BOTH from the payload. Please note that we exclude this field when updating a variation parent. | | date_on_sale_to_gmt | | Listing WooCommerce > Promotion Date End | We need to convert it into an ISO8601 format. We only need to send if both fields are filled. If one or both are not filled, we exclude them BOTH from the payload. Please note that we exclude this field when updating a variation parent. | | manage_stock | | | Hardcoded to “true”. Please note that we exclude this field when updating a variation parent. | | stock_quantity | | Listing > Quantity | Please note that we exclude this field when updating a variation parent. | | sold_individually | | Listing WooCommerce > Sold Individually | New field! This should be a tickbox field and if checked, we send “true”, if unchecked we send “false”. Default should be unchecked. Please note that we exclude this field when updating a variation parent. | | weight | | Product > Weight | We need a logic based on the Channel > Country : -If it is United States we need to convert to lbs -If it is any other country, we send it without converting. | | dimensions | | | | | | length | Product > Length | We need a logic based on the Channel > Country : -If it is United States we need to convert to inches -If it is any other country, we send it without converting. | | | width | Product > Width | We need a logic based on the Channel > Country : -If it is United States we need to convert to inches -If it is any other country, we send it without converting. | | | height | Product > Height | We need a logic based on the Channel > Country : -If it is United States we need to convert to inches -If it is any other country, we send it without converting. | | categories | | | | | | id | Listing > Primary Category ID AND Listing WooCommerce > More Categories | If we have more than one category we need to send them as separate objects. If we have more than 1 record in More Categories they will be separated by comma. We need to split and send them as separate objects. We will have the name filled but we need to send the id. We need to have the same taxonomy validation for More Categories as Primary Category ID. | | tags | | | | | | name | Listing WooCommerce > Tags | New field! This should be a field with the possibility to “add more features”. Similar to the Item/Variation specifics fields. The only difference is that here we will have only one box for input instead of two. Then each row should be sent as separate object into the tags array | | images | | | | | | src | Listing Image + More Images OR All Images (Leading + Additional | As per abstraction - Images Handling Additional Explanation We need to have a logic -If we are updating a simple product, we send All Images -If we are updating a variation parent, we send Listing Image + More Images | | attributes | | | Each separate attribute needs to be in a separate attributes array | | | id | Listing > Item Specific Name | | | | variation | | -If we are updating simple product, we send this as “false” -If we are updating a variation parent, we send this as “true” | | | visible | | Hardcoded as “true” | | | options | Listing > Item Specific Value | Please note that if we are sending a variation parent, we MUST include all of the variations’ item specific attributes. So for example if we have a variation that varies by Size and we have values of S,M,L across the variation products, then we need to include them all when updating the variation parent (just like in the payload example. If it is a simple product, it will have just one option and we still need to add it in the options array. | | brands | | | Each separate brand (if more than 1) needs to be in a separate brands array | | | id | Product > Brand AND Listing WooCommerce > More Brands | If we have more than one brand we need to send them as separate objects. If we have more than 1 record in More Brands they will be separated by comma. We need to split and send them as separate objects. We will have the name filled but we need to send the id. We need to have the same taxonomy validation for More Brands as Product > Brand |

Example response :

{
    "id": 145,
    "name": "Hoodie with VariantNEW",
    "slug": "hoodie-with-variantnew-4",
    "permalink": "https://sociable-sable-b1ae91.instawp.xyz/product/hoodie-with-variantnew-4/",
    "date_created": "2025-06-03T08:08:44",
    "date_created_gmt": "2025-06-03T08:08:44",
    "date_modified": "2025-06-03T08:08:44",
    "date_modified_gmt": "2025-06-03T08:08:44",
    "type": "variable",
    "status": "publish",
    "featured": false,
    "catalog_visibility": "visible",
    "description": "<p style=\"text-align: left\">Hoody very best and ogod</p>",
    "short_description": "The best product in the whole world",
    "sku": "",
    "price": "",
    "regular_price": "",
    "sale_price": "",
    "date_on_sale_from": null,
    "date_on_sale_from_gmt": null,
    "date_on_sale_to": null,
    "date_on_sale_to_gmt": null,
    "on_sale": false,
    "purchasable": false,
    "total_sales": 0,
    "virtual": false,
    "downloadable": false,
    "downloads": [],
    "download_limit": -1,
    "download_expiry": -1,
    "external_url": "",
    "button_text": "",
    "tax_status": "taxable",
    "tax_class": "",
    "manage_stock": true,
    "stock_quantity": 20,
    "backorders": "no",
    "backorders_allowed": false,
    "backordered": false,
    "low_stock_amount": null,
    "sold_individually": true,
    "weight": "123",
    "dimensions": {
        "length": "22",
        "width": "12",
        "height": "5"
    },
    "shipping_required": true,
    "shipping_taxable": true,
    "shipping_class": "",
    "shipping_class_id": 0,
    "reviews_allowed": true,
    "average_rating": "0",
    "rating_count": 0,
    "upsell_ids": [],
    "cross_sell_ids": [],
    "parent_id": 0,
    "purchase_note": "",
    "categories": [
        {
            "id": 15,
            "name": "Uncategorized",
            "slug": "uncategorized"
        }
    ],
    "tags": [
        {
            "id": 41,
            "name": "blabla",
            "slug": "blabla"
        }
    ],
    "images": [
        {
            "id": 144,
            "date_created": "2025-06-03T08:08:44",
            "date_created_gmt": "2025-06-03T08:08:44",
            "date_modified": "2025-06-03T08:08:44",
            "date_modified_gmt": "2025-06-03T08:08:44",
            "src": "https://sociable-sable-b1ae91.instawp.xyz/wp-content/uploads/2025/06/texture-2613518_1280-3.jpg",
            "name": "texture-2613518_1280-3.jpg",
            "alt": ""
        }
    ],
    "attributes": [
        {
            "id": 1,
            "name": "Size",
            "slug": "pa_size",
            "position": 0,
            "visible": true,
            "variation": true,
            "options": [
                "M",
                "S"
            ]
        }
    ],
    "default_attributes": [],
    "variations": [],
    "grouped_products": [],
    "menu_order": 0,
    "price_html": "",
    "related_ids": [
        105,
        112,
        97,
        93,
        110
    ],
    "meta_data": [],
    "stock_status": "instock",
    "has_options": true,
    "post_password": "",
    "global_unique_id": "",
    "permalink_template": "https://sociable-sable-b1ae91.instawp.xyz/product/%pagename%/",
    "generated_slug": "hoodie-with-variantnew-4",
    "brands": [
        {
            "id": 24,
            "name": "Guma",
            "slug": "guma"
        }
    ],
    "_links": {
        "self": [
            {
                "href": "https://sociable-sable-b1ae91.instawp.xyz/wp-json/wc/v3/products/145",
                "targetHints": {
                    "allow": [
                        "GET",
                        "POST",
                        "PUT",
                        "PATCH",
                        "DELETE"
                    ]
                }
            }
        ],
        "collection": [
            {
                "href": "https://sociable-sable-b1ae91.instawp.xyz/wp-json/wc/v3/products"
            }
        ]
    }
}

We don’t need to map anything from the response, if success we need to act as per the abstraction - Product Listing general requirements. If there was an error, we want to store the message into Listing > Update Item Error and set the Listing > List/Update the whole item = Error.

Variant Update

API Call : POST /wp-json/wc/v3/products/{product_id}/variations/batch API Docs : https://woocommerce.github.io/woocommerce-rest-api-docs/?shell#batch-update-product-variations

We use this call when we need to update a product that is part of a variation. Remember we still need to update the parent product but this time as part of the variation.

The {productId} we pick from Listing > Channel Item ID

Example call :

{
  "update": [
    {
    "id": 77,    
    "name": "Hoodie with Variant - L",    
    "description": "LLLLLLLLLLLLLLLLLLL",    
    "sku": "SKU789",
    "global_unique_id": "",    
    "regular_price": "110",
    "sale_price": "99",
    "date_on_sale_from_gmt": null,
    "date_on_sale_to_gmt": null,
    "manage_stock": true,
    "stock_quantity": 20,
    "weight": "123",
    "dimensions": {
        "length": "22",
        "width": "12",
        "height": "5"
    },
    "categories": [
        {
            "id": 15
        }
    ],
    "tags": [],
    "images": [
        {
            "src": "https://cdn.pixabay.com/photo/2017/08/09/04/53/texture-2613518_1280.jpg"
        }
    ],
    "attributes": [
      {
      "id": 1,
      "option": "L"
    }
    ],
    "brands": [
        {
            "id": 24
        }
    ]
},
{
    "id": 78,
    "name": "Hoodie with Variant - M",    
    "description": "Mmmmmmmmmmmmmmmmm",    
    "sku": "SKU123",
    "global_unique_id": "",    
    "regular_price": "150",
    "sale_price": "129",
    "date_on_sale_from_gmt": null,
    "date_on_sale_to_gmt": null,
    "manage_stock": true,
    "stock_quantity": 20,
    "weight": "123",
    "dimensions": {
        "length": "22",
        "width": "12",
        "height": "5"
    },
    "categories": [
        {
            "id": 15
        }
    ],
    "tags": [],
    "images": [
        {
            "src": "https://cdn.pixabay.com/photo/2017/08/09/04/53/texture-2613518_1280.jpg"
        }
    ],
    "attributes": [
      {
      "id": 1,
      "option": "M"
    }
    ],
    "brands": [
        {
            "id": 24
        }
    ]
}

  ]
}

Mapping :

WooCommerce Field MCPro Field Notes
name Listing > Title
description Listing > Description Description is html friendly.
The design template logic remains so if we have design template we pick as per abstraction.
sku Product > SKU Please note. If we are sending the parent’s info, we need to send this as empty. We can find out if we are sending the parent’s details if we have Listing WooCommerce > Is Parent flag to Yes.
global_unique_id Product Account >
Marketplace EAN OR Product > EAN OR Product > Barcode OR Product > MPN OR Product >UPC Product Account is with priority. The priority in the other fields is : EAN, Barcode, MPN, UPC. Please note. If we are sending the parent’s info, we need to send this as empty. We can find out if we are sending the parent’s details if we have Listing WooCommerce > Is Parent flag to Yes. regular_price Listing > RRP We need to have a logic and if RRP is empty, we send Price as regular_price and sale_price as empty.
sale_price Listing > Price Please note that it looks like Woo does not have any validations for prices whatsoever, so it is possible that we send sale_price higher than regular_price. They will still return success response but the higher sale price will not show, only the lower price will show. We want to have internal validation and if we have Price> RRP, we want to store internal error.
date_on_sale_from_gmt Listing WooCommerce > Promotion Date Start We need to convert it into an ISO8601 format. We only need to send if both fields are filled. If one or both are not filled, we exclude them BOTH from the payload.
date_on_sale_to_gmt Listing WooCommerce > Promotion Date End We need to convert it into an ISO8601 format. We only need to send if both fields are filled. If one or both are not filled, we exclude them BOTH from the payload.
manage_stock Hardcoded to “true”.
stock_quantity Listing > Quantity
sold_individually Listing WooCommerce > Sold Individually New field! This should be a tickbox field and if checked, we send “true”, if unchecked we send “false”. Default should be unchecked.
weight Product > Weight We need a logic based on the Channel > Country :
-If it is United States we need to convert to lbs -If it is any other country, we send it without converting. dimensions length Product > Length We need a logic based on the Channel > Country : -If it is United States we need to convert to inches -If it is any other country, we send it without converting. width Product > Width We need a logic based on the Channel > Country : -If it is United States we need to convert to inches -If it is any other country, we send it without converting. height Product > Height We need a logic based on the Channel > Country : -If it is United States we need to convert to inches -If it is any other country, we send it without converting. categories id Listing > Primary Category ID AND Listing WooCommerce > More Categories If we have more than one category we need to send them as separate objects. If we have more than 1 record in More Categories they will be separated by comma. We need to split and send them as separate objects. We will have the name filled but we need to send the id. We need to have the same taxonomy validation for More Categories as Primary Category ID. tags name Listing WooCommerce > Tags New field! This should be a field with the possibility to “add more features”. Similar to the Item/Variation specifics fields. The only difference is that here we will have only one box for input instead of two. Then each row should be sent as separate object into the tags array images src Main Image As per abstraction - Images Handling Additional Explanation
attributes Each separate attribute needs to be in a separate attributes array
id Listing > Item Specific Name We will have the name, but we send the id
option Listing > Item Specific Value
brands Each separate brand (if more than 1) needs to be in a separate brands array
id Product > Brand

AND Listing WooCommerce > More Brands | If we have more than one brand we need to send them as separate objects. If we have more than 1 record in More Brands they will be separated by comma. We need to split and send them as separate objects. We will have the name filled but we need to send the id. We need to have the same taxonomy validation for More Brands as Product > Brand |

Example response with error :

{
    "update": [
        {
            "id": 77,
            "type": "variation",
            "date_created": "2025-05-21T13:45:04",
            "date_created_gmt": "2025-05-21T13:45:04",
            "date_modified": "2025-06-03T08:32:54",
            "date_modified_gmt": "2025-06-03T08:32:54",
            "description": "<p>LLLLLLLLLLLLLLLLLLL</p>\n",
            "permalink": "https://sociable-sable-b1ae91.instawp.xyz/product/hoodie-with-stripes-2/?attribute_pa_size=l",
            "sku": "SKU789",
            "global_unique_id": "",
            "price": "99",
            "regular_price": "110",
            "sale_price": "99",
            "date_on_sale_from": null,
            "date_on_sale_from_gmt": null,
            "date_on_sale_to": null,
            "date_on_sale_to_gmt": null,
            "on_sale": true,
            "status": "publish",
            "purchasable": true,
            "virtual": false,
            "downloadable": false,
            "downloads": [],
            "download_limit": -1,
            "download_expiry": -1,
            "tax_status": "taxable",
            "tax_class": "parent",
            "manage_stock": true,
            "stock_quantity": 20,
            "stock_status": "instock",
            "backorders": "no",
            "backorders_allowed": false,
            "backordered": false,
            "low_stock_amount": null,
            "weight": "123",
            "dimensions": {
                "length": "22",
                "width": "12",
                "height": "5"
            },
            "shipping_class": "",
            "shipping_class_id": 0,
            "image": {
                "id": 63,
                "date_created": "2025-05-20T13:13:53",
                "date_created_gmt": "2025-05-20T13:13:53",
                "date_modified": "2025-05-20T13:13:53",
                "date_modified_gmt": "2025-05-20T13:13:53",
                "src": "https://sociable-sable-b1ae91.instawp.xyz/wp-content/uploads/2025/05/1715726245686.jpg",
                "name": "1715726245686",
                "alt": ""
            },
            "attributes": [
                {
                    "id": 1,
                    "name": "Size",
                    "slug": "pa_size",
                    "option": "L"
                }
            ],
            "menu_order": 1,
            "meta_data": [],
            "name": "L",
            "parent_id": 74,
            "_links": {
                "self": [
                    {
                        "href": "https://sociable-sable-b1ae91.instawp.xyz/wp-json/wc/v3/products/74/variations/77",
                        "targetHints": {
                            "allow": [
                                "GET",
                                "POST",
                                "PUT",
                                "PATCH",
                                "DELETE"
                            ]
                        }
                    }
                ],
                "collection": [
                    {
                        "href": "https://sociable-sable-b1ae91.instawp.xyz/wp-json/wc/v3/products/74/variations"
                    }
                ],
                "up": [
                    {
                        "href": "https://sociable-sable-b1ae91.instawp.xyz/wp-json/wc/v3/products/74"
                    }
                ]
            }
        },
        {
            "id": 78,
            "error": {
                "code": "product_invalid_sku",
                "message": "Invalid or duplicated SKU.",
                "data": {
                    "status": 400,
                    "resource_id": 79,
                    "unique_sku": "SKU456-1"
                }
            }
        }
    ]
}

From the success response we need to act as per the abstraction Product Listing general requirements.

From the error response we want to store the message into Listing > Update Item Error and set the Listing > List/Update the whole item = Error.

Is this article helpful?
0 0 0