Marketplaces / WooCommerce Technical Sccope / WooCommerce Product Management / WooCommerce Create Products

WooCommerce Create Products

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

Just like all other product related flows, when we are talking about creating products on WooCommerce we have to consider product creation and variant creation. There are two separate flows for that.

Simple products are straightforward - one API call creates a complete, purchasable product.

Variable products require a two step creation because they're essentially a container(parent) holding multiple variations. For variable products, customers actually purchase the variations, not the parent product. The parent product serves as a container and cannot be purchased directly. This means that because of our structure, we will need to implement a logic and if we have a variation product to be created, we will have to use the product with the lowest ID in MCPro as a parent product and then make sure to send this same product information as variant.

All triggers, validations etc. which are not specifically mentioned, are as per our abstraction for Product Creation. Product Listing general requirements

Product create

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

Example request body :

{
    "name": "Hoodie with VariantNEW",
    "type": "variable",
    "description": "<p style=\"text-align: left;\">Hoody very best and ogod</p>",
    "short_description": "The best product in the whole world",
    "sku": "STAMAT14213135",
    "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,
    "sold_individually": "true",
    "weight": "123",
    "dimensions": {
        "length": "22",
        "width": "12",
        "height": "5"
    },
    "categories": [
        {
         "id": 15
        }
    ],
    "tags": [
        {
        "name": "blabla"
        }
    ],
    "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
        }
    ]
}

Request Mapping :

WooCommerce Field MCPro Field Notes
name Listing > Title
type We need a logic.

-If we are sending a single product, not part of a variation we send this hardcoded as “simple” -If we are sending a variation, we send this as “variable” | | 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 creating 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 creating 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 creating 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 creating a variation parent. | | manage_stock | | | Hardcoded to “true”. Please note that we exclude this field when creating a variation parent. | | stock_quantity | | Listing > Quantity | Please note that we exclude this field when creating 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 creating 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 in a table field style. 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 PictureUrls 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 creating a simple product, we send All Images -If we are creating 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 creating simple product, we send this as “false” -If we are creating 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 creating 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 Brandss as Product > Brand |

Example Success Response :

{
    "id": 130,
    "name": "Hoodie with VariantNEW",
    "slug": "hoodie-with-variantnew-3",
    "permalink": "https://sociable-sable-b1ae91.instawp.xyz/product/hoodie-with-variantnew-3/",
    "date_created": "2025-05-29T14:38:03",
    "date_created_gmt": "2025-05-29T14:38:03",
    "date_modified": "2025-05-29T14:38:03",
    "date_modified_gmt": "2025-05-29T14:38:03",
    "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": "STAMAT14213135",
    "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": 129,
            "date_created": "2025-05-29T14:38:02",
            "date_created_gmt": "2025-05-29T14:38:02",
            "date_modified": "2025-05-29T14:38:03",
            "date_modified_gmt": "2025-05-29T14:38:03",
            "src": "https://sociable-sable-b1ae91.instawp.xyz/wp-content/uploads/2025/05/texture-2613518_1280-16.jpg",
            "name": "texture-2613518_1280-16.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": [
        97,
        95,
        114,
        120,
        93
    ],
    "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-3",
    "brands": [
        {
            "id": 24,
            "name": "Guma",
            "slug": "guma"
        }
    ],
    "_links": {
        "self": [
            {
                "href": "https://sociable-sable-b1ae91.instawp.xyz/wp-json/wc/v3/products/130",
                "targetHints": {
                    "allow": [
                        "GET",
                        "POST",
                        "PUT",
                        "PATCH",
                        "DELETE"
                    ]
                }
            }
        ],
        "collection": [
            {
                "href": "https://sociable-sable-b1ae91.instawp.xyz/wp-json/wc/v3/products"
            }
        ]
    }
}

Example Error Response :

{
    "code": "product_invalid_sku",
    "message": "Invalid or duplicated SKU.",
    "data": {
        "status": 400,
        "resource_id": 130,
        "unique_sku": "STAMAT14213135-1"
    }
}

From the success response we only need to store the id. If we have created a simple product, want to store it into Listing > Channel Item ID and mark the product as created as per the abstraction Product Listing general requirements

If we have created a variation parent, we want to mark it as such, by raising a flag in Listing WooCommerce > Is Parent(new field) and store the id into Listing > Channel Item ID. This way we will use the field as a marker and identify that this product is the variation parent and that we still need to send its’ product information when creating the variants. At this point we don’t want to touch any statuses on the product.

To hopefully make it easier to understand, here is an example : We have a variation group consisting of SKUs 1, 2, 3, 4, and 5. Our goal is to create this variation in WooCommerce. During the product creation process, we select the parent SKU (which should be SKU 1, the one with the lowest ID) and initiate its creation. Once it’s successfully created, we store its ID in the two fields mentioned earlier. Now, we need to add the variants. Since SKU 1 serves only as the parent and cannot be purchased on its own, we must include it in the add variation SKUs call. Therefore, we send SKUs 1, 2, 3, 4, and 5 for variation creation. There are some specific details to consider when sending the parent SKU as a variant, which will be explained in the mapping section below.

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.

Variant Create

After successfully creating the parent, we now need to add the variants in it. Remember we also need to send the parent’s information as a variant in order for it to be sold.

For this call we need to pick all of the variants that are part of the parent’s variation (which we have already created) and push them for creation. In order to identify whether we have variations to create, we want to have a few checks : whether the product is part of a variation group and whether we have created a parent within that variation group. If these two conditions match, this means we have to create a variation.

API Call : POST /wp-json/wc/v3/products/{productId}/variations API Docs : https://woocommerce.github.io/woocommerce-rest-api-docs/?shell#create-a-product-variation

The {productId} we pick from the parent’s Listing > Channel Item ID.

Example call :

{
    "name": "Hoodie with Variant",    
    "description": "<p style=\"text-align: left;\">Hoody very best and ogod</p>",    
    "sku": "stana123",
    "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": "S"
    }
    ],
    "brands": [
        {
            "id": 24
        }
    ]
}

Call 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 Success Response :

{
    "id": 139,
    "type": "variation",
    "date_created": "2025-06-02T08:28:17",
    "date_created_gmt": "2025-06-02T08:28:17",
    "date_modified": "2025-06-02T08:28:17",
    "date_modified_gmt": "2025-06-02T08:28:17",
    "description": "<p style=\"text-align: left\">Hoody very best and ogod</p>\n",
    "permalink": "https://sociable-sable-b1ae91.instawp.xyz/product/hoodie-with-varianto-2/?attribute_pa_size=xs",
    "sku": "stan111133",
    "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": "103",
    "dimensions": {
        "length": "33",
        "width": "15",
        "height": "157"
    },
    "shipping_class": "",
    "shipping_class_id": 0,
    "image": null,
    "attributes": [
        {
            "id": 1,
            "name": "Size",
            "slug": "pa_size",
            "option": "XS"
        }
    ],
    "menu_order": 0,
    "meta_data": [],
    "name": "XS",
    "parent_id": 116,
    "_links": {
        "self": [
            {
                "href": "https://sociable-sable-b1ae91.instawp.xyz/wp-json/wc/v3/products/116/variations/139",
                "targetHints": {
                    "allow": [
                        "GET",
                        "POST",
                        "PUT",
                        "PATCH",
                        "DELETE"
                    ]
                }
            }
        ],
        "collection": [
            {
                "href": "https://sociable-sable-b1ae91.instawp.xyz/wp-json/wc/v3/products/116/variations"
            }
        ],
        "up": [
            {
                "href": "https://sociable-sable-b1ae91.instawp.xyz/wp-json/wc/v3/products/116"
            }
        ]
    }
}

Example Error Response :

{
    "code": "woocommerce_rest_product_variation_invalid_parent",
    "message": "Cannot set attributes due to invalid parent product.",
    "data": {
        "status": 404
    }
}

From the success response we only need to store the id into Listing WooCommerce > Variant ID and 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