Product listing quality audit, SEO improvements, missing field detection, and catalog health scoring using direct Wix REST API calls.
From wix-ecom-coworknpx claudepluginhub itayher/wix-ecom-cowork --plugin wix-ecom-coworkThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Product listing quality audit, SEO improvements, missing field detection, and catalog health scoring using direct Wix REST API calls.
df7c18eb-009b-4868-9891-15e19dddbe67${API_KEY}${SITE_ID}API Call:
curl -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}' | jq '[.products[] | select(.description == null or .description == "" or (.description | length) < 50)] | map({id, name, description_length: (.description // "" | length)})'
curl -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}' | jq '[.products[] | select(.media.mainMedia == null or .media.mainMedia.image == null)] | map({id, name, has_image: false})'
curl -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}' | jq '[.products[] | select(.price == null or (.price | tonumber) == 0)] | map({id, name, price})'
curl -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}' | jq '[.products[] | select(.sku == null or .sku == "")] | map({id, name, sku})'
#!/bin/bash
products=$(curl -s -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}')
echo "CATALOG HEALTH REPORT"
echo "===================="
echo ""
# Calculate quality scores
echo "$products" | jq -r '
[.products[] | {
id,
name,
has_description: ((.description // "" | length) >= 50),
has_image: (.media.mainMedia.image != null),
has_price: ((.price // 0 | tonumber) > 0),
has_sku: (.sku != null and .sku != ""),
has_brand: (.brand != null and .brand != ""),
has_ribbon: (.ribbon != null and .ribbon != ""),
description_length: (.description // "" | length)
}] |
map(. + {
quality_score: (
(if .has_description then 25 else 0 end) +
(if .has_image then 25 else 0 end) +
(if .has_price then 20 else 0 end) +
(if .has_sku then 15 else 0 end) +
(if .has_brand then 10 else 0 end) +
(if .has_ribbon then 5 else 0 end)
)
}) |
# Overall catalog metrics
{
total_products: length,
avg_quality_score: ([.[].quality_score] | add / length),
excellent: ([.[] | select(.quality_score >= 80)] | length),
good: ([.[] | select(.quality_score >= 60 and .quality_score < 80)] | length),
needs_improvement: ([.[] | select(.quality_score >= 40 and .quality_score < 60)] | length),
poor: ([.[] | select(.quality_score < 40)] | length),
missing_descriptions: ([.[] | select(.has_description == false)] | length),
missing_images: ([.[] | select(.has_image == false)] | length),
missing_skus: ([.[] | select(.has_sku == false)] | length),
missing_brands: ([.[] | select(.has_brand == false)] | length)
} |
"Total Products: \(.total_products)",
"Average Quality Score: \(.avg_quality_score | floor)/100",
"",
"Score Distribution:",
" Excellent (80-100): \(.excellent) products",
" Good (60-79): \(.good) products",
" Needs Improvement (40-59): \(.needs_improvement) products",
" Poor (0-39): \(.poor) products",
"",
"Missing Fields:",
" Descriptions: \(.missing_descriptions)",
" Images: \(.missing_images)",
" SKUs: \(.missing_skus)",
" Brands: \(.missing_brands)"
'
#!/bin/bash
products=$(curl -s -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}')
echo "TOP PRIORITY PRODUCTS TO FIX"
echo "============================"
echo ""
echo "$products" | jq -r '
[.products[] | {
id,
name,
has_description: ((.description // "" | length) >= 50),
has_image: (.media.mainMedia.image != null),
has_price: ((.price // 0 | tonumber) > 0),
quality_score: (
(if ((.description // "" | length) >= 50) then 25 else 0 end) +
(if (.media.mainMedia.image != null) then 25 else 0 end) +
(if ((.price // 0 | tonumber) > 0) then 20 else 0 end) +
(if (.sku != null and .sku != "") then 15 else 0 end) +
(if (.brand != null and .brand != "") then 10 else 0 end) +
(if (.ribbon != null and .ribbon != "") then 5 else 0 end)
),
issues: [
(if ((.description // "" | length) < 50) then "❌ Missing/short description" else null end),
(if (.media.mainMedia.image == null) then "❌ No product image" else null end),
(if ((.price // 0 | tonumber) == 0) then "❌ No price set" else null end),
(if (.sku == null or .sku == "") then "⚠️ Missing SKU" else null end),
(if (.brand == null or .brand == "") then "⚠️ Missing brand" else null end)
] | map(select(. != null))
}] |
map(select(.quality_score < 60)) |
sort_by(.quality_score) |
.[:10] |
.[] |
"\(.name) (Score: \(.quality_score)/100)",
(.issues | map(" " + .) | .[]),
""
'
curl -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}' | jq '[.products[] | select(.seoData == null or .seoData.tags == null or (.seoData.tags | length) == 0)] | map({id, name, has_seo: false})'
#!/bin/bash
# Get product
PRODUCT_ID="product-abc123"
product=$(curl -s -X GET "https://www.wixapis.com/stores/v1/products/${PRODUCT_ID}" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}")
product_name=$(echo "$product" | jq -r '.product.name')
product_desc=$(echo "$product" | jq -r '.product.description // ""')
# Generate SEO metadata (example - use AI or template)
seo_title="${product_name} - Buy Online | Your Store Name"
seo_desc=$(echo "$product_desc" | cut -c1-155)
echo "Generated SEO for: $product_name"
echo "Title: $seo_title"
echo "Description: $seo_desc"
echo ""
echo "Apply with:"
echo "curl -X PATCH \"https://www.wixapis.com/stores/v1/products/${PRODUCT_ID}\" \\"
echo " -H \"Authorization: \${API_KEY}\" \\"
echo " -H \"wix-site-id: \${SITE_ID}\" \\"
echo " -H \"Content-Type: application/json\" \\"
echo " -d '{
\"product\": {
\"seoData\": {
\"tags\": [
{\"type\": \"title\", \"children\": \"'"$seo_title"'\", \"custom\": true},
{\"type\": \"meta\", \"props\": {\"name\": \"description\", \"content\": \"'"$seo_desc"'\"}}
]
}
}
}'"
#!/bin/bash
products=$(curl -s -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}')
echo "Products with Non-Optimized Slugs"
echo "================================="
echo ""
echo "$products" | jq -r '
.products[] |
select(.slug | contains("product-") or contains("_") or contains("--")) |
"❌ \(.name) → /product-page/\(.slug)"
'
curl -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}' | jq '[.products[] | {id, name, image_count: ((.media.items // []) | length)}] | sort_by(.image_count)'
#!/bin/bash
products=$(curl -s -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}')
echo "Products Needing More Images (< 3 images)"
echo "========================================="
echo ""
echo "$products" | jq -r '
.products[] |
{
id,
name,
price,
image_count: ((.media.items // []) | length)
} |
select(.image_count < 3) |
"• \(.name) - \(.image_count) image(s) - $\(.price)"
'
#!/bin/bash
products=$(curl -s -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}')
echo "Products Missing Image Alt Text"
echo "==============================="
echo ""
echo "$products" | jq -r '
.products[] |
select(.media.mainMedia.image != null) |
select(.media.mainMedia.image.altText == null or .media.mainMedia.image.altText == "") |
"• \(.name) - Main image has no alt text"
'
curl -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}' | jq '[.products[] | select(.comparePrice == null or (.comparePrice | tonumber) == 0)] | map({id, name, price, comparePrice})'
#!/bin/bash
products=$(curl -s -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}')
echo "Pricing Issues"
echo "=============="
echo ""
echo "$products" | jq -r '
.products[] |
{
name,
price: (.price // 0 | tonumber),
comparePrice: (.comparePrice // 0 | tonumber),
cost: (.cost // 0 | tonumber)
} |
# Check for various pricing issues
if (.price == 0) then
"❌ \(.name) - Price is $0"
elif (.comparePrice > 0 and .comparePrice <= .price) then
"⚠️ \(.name) - Compare price (\(.comparePrice)) not higher than price (\(.price))"
elif (.cost > 0 and .price < .cost) then
"❌ \(.name) - Selling below cost! Price: $\(.price), Cost: $\(.cost)"
elif (.cost > 0 and ((.price - .cost) / .price * 100) < 20) then
"⚠️ \(.name) - Low margin: \(((.price - .cost) / .price * 100) | floor)%"
else
empty
end
'
#!/bin/bash
products=$(curl -s -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}')
collections=$(curl -s -X POST "https://www.wixapis.com/stores/v1/collections/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{"query": {"paging": {"limit": 50}}}')
echo "Average Margins by Collection"
echo "============================="
echo ""
echo "$products" | jq -r --argjson cols "$(echo "$collections" | jq '.collections')" '
[.products[] | {
name,
price: (.price // 0 | tonumber),
cost: (.cost // 0 | tonumber),
margin: (if (.cost // 0 | tonumber) > 0 and (.price // 0 | tonumber) > 0 then ((.price - .cost) / .price * 100) else null end),
collectionIds: .collectionIds // []
}] |
map(select(.margin != null)) |
# Group by first collection
group_by(.collectionIds[0]) |
map({
collection_id: (.[0].collectionIds[0] // "No Collection"),
avg_margin: ([.[].margin] | add / length),
products: length
}) |
.[] |
"\(.collection_id): \(.avg_margin | floor)% avg margin (\(.products) products)"
'
#!/bin/bash
products=$(curl -s -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}')
echo "Products with Short Descriptions (< 100 chars)"
echo "=============================================="
echo ""
echo "$products" | jq -r '
.products[] |
{
name,
desc_length: (.description // "" | length)
} |
select(.desc_length < 100) |
"• \(.name) - \(.desc_length) characters"
'
#!/bin/bash
products=$(curl -s -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}')
echo "Products Missing Key Fields"
echo "==========================="
echo ""
echo "$products" | jq -r '
.products[] |
{
name,
missing: [
(if (.description == null or (.description | length) < 50) then "description" else null end),
(if (.brand == null or .brand == "") then "brand" else null end),
(if (.weight == null or (.weight | tonumber) == 0) then "weight" else null end),
(if (.ribbon == null or .ribbon == "") then "ribbon/badge" else null end),
(if (.sku == null or .sku == "") then "SKU" else null end)
] | map(select(. != null))
} |
select(.missing | length > 0) |
"• \(.name): Missing \(.missing | join(", "))"
'
#!/bin/bash
products=$(curl -s -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}')
echo "$products" | jq -r '.products[] | select(.seoData == null) | .id' | while read product_id; do
# Get product details
product=$(curl -s -X GET "https://www.wixapis.com/stores/v1/products/${product_id}" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}")
name=$(echo "$product" | jq -r '.product.name')
desc=$(echo "$product" | jq -r '.product.description // ""' | cut -c1-155)
# Update with SEO data
curl -s -X PATCH "https://www.wixapis.com/stores/v1/products/${product_id}" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d "{
\"product\": {
\"seoData\": {
\"tags\": [
{\"type\": \"title\", \"children\": \"$name - Buy Online\", \"custom\": true},
{\"type\": \"meta\", \"props\": {\"name\": \"description\", \"content\": \"$desc\"}}
]
}
}
}" > /dev/null
echo "✓ Added SEO data to: $name"
sleep 0.2
done
#!/bin/bash
products=$(curl -s -X POST "https://www.wixapis.com/stores/v1/products/query" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d '{
"query": {
"filter": "{\"visible\": true}",
"paging": {"limit": 100}
}
}')
echo "$products" | jq -r '.products[] | select(.media.mainMedia.image != null and (.media.mainMedia.image.altText == null or .media.mainMedia.image.altText == "")) | {id, name, image: .media.mainMedia.image.url}' | jq -c '.' | while read product; do
product_id=$(echo "$product" | jq -r '.id')
product_name=$(echo "$product" | jq -r '.name')
image_url=$(echo "$product" | jq -r '.image')
# Update with alt text
curl -s -X PATCH "https://www.wixapis.com/stores/v1/products/${product_id}" \
-H "Authorization: ${API_KEY}" \
-H "wix-site-id: ${SITE_ID}" \
-H "Content-Type: application/json" \
-d "{
\"product\": {
\"media\": {
\"mainMedia\": {
\"image\": {
\"url\": \"$image_url\",
\"altText\": \"$product_name\"
}
}
}
}
}" > /dev/null
echo "✓ Added alt text to: $product_name"
sleep 0.2
done