Claude-skills shopify-products
git clone https://github.com/jezweb/claude-skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/jezweb/claude-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/shopify/skills/shopify-products" ~/.claude/skills/jezweb-claude-skills-shopify-products && rm -rf "$T"
plugins/shopify/skills/shopify-products/SKILL.mdShopify Products
Create, update, and bulk-import Shopify products. Produces live products in the store via the GraphQL Admin API or CSV import.
Prerequisites
- Admin API access token (use the shopify-setup skill if not configured)
- Store URL and API version from
orshopify.config.json.dev.vars
Workflow
Step 1: Gather Product Data
Determine what the user wants to create or update:
- Product basics: title, description (HTML), product type, vendor, tags
- Variants: options (size, colour, material), prices, SKUs, inventory quantities
- Images: URLs to upload, or local files
- SEO: page title, meta description, URL handle
- Organisation: collections, product type, tags
Accept data from:
- Direct conversation (user describes products)
- Spreadsheet/CSV file (user provides a file)
- Website scraping (user provides a URL to extract from)
Step 2: Choose Method
| Scenario | Method |
|---|---|
| 1-5 products | GraphQL mutations |
| 6-20 products | GraphQL with batching |
| 20+ products | CSV import via admin |
| Updates to existing | GraphQL mutations |
| Inventory adjustments | mutation |
Step 3a: Create via GraphQL (Recommended)
productCreate
mutation productCreate($product: ProductCreateInput!) { productCreate(product: $product) { product { id title handle status variants(first: 100) { edges { node { id title price sku inventoryQuantity } } } } userErrors { field message } } }
Variables:
{ "product": { "title": "Example T-Shirt", "descriptionHtml": "<p>Premium cotton tee</p>", "vendor": "My Brand", "productType": "T-Shirts", "tags": ["summer", "cotton"], "status": "DRAFT", "options": ["Size", "Colour"], "variants": [ { "optionValues": [ {"optionName": "Size", "name": "S"}, {"optionName": "Colour", "name": "Black"} ], "price": "29.95", "sku": "TSHIRT-S-BLK", "inventoryPolicy": "DENY", "inventoryItem": { "tracked": true } }, { "optionValues": [ {"optionName": "Size", "name": "M"}, {"optionName": "Colour", "name": "Black"} ], "price": "29.95", "sku": "TSHIRT-M-BLK" }, { "optionValues": [ {"optionName": "Size", "name": "L"}, {"optionName": "Colour", "name": "Black"} ], "price": "29.95", "sku": "TSHIRT-L-BLK" } ], "seo": { "title": "Example T-Shirt | My Brand", "description": "Premium cotton tee in multiple sizes" } } }
Curl example:
curl -s https://{store}/admin/api/2025-01/graphql.json \ -H "Content-Type: application/json" \ -H "X-Shopify-Access-Token: {token}" \ -d '{"query": "mutation productCreate($product: ProductCreateInput!) { productCreate(product: $product) { product { id title } userErrors { field message } } }", "variables": { ... }}'
Batching multiple products: Create products sequentially with a short delay between each to respect rate limits (1,000 cost points/second).
productUpdate
mutation productUpdate($input: ProductInput!) { productUpdate(input: $input) { product { id title } userErrors { field message } } }
Variables include
id (required) plus any fields to update.
productDelete
mutation productDelete($input: ProductDeleteInput!) { productDelete(input: $input) { deletedProductId userErrors { field message } } }
productVariantsBulkCreate
Add variants to an existing product:
mutation productVariantsBulkCreate($productId: ID!, $variants: [ProductVariantsBulkInput!]!) { productVariantsBulkCreate(productId: $productId, variants: $variants) { productVariants { id title price } userErrors { field message } } }
productVariantsBulkUpdate
mutation productVariantsBulkUpdate($productId: ID!, $variants: [ProductVariantsBulkInput!]!) { productVariantsBulkUpdate(productId: $productId, variants: $variants) { productVariants { id title price } userErrors { field message } } }
Step 3b: Bulk Import via CSV
For 20+ products, generate a CSV and import through Shopify admin.
CSV Column Reference
Required columns:
| Column | Description | Example |
|---|---|---|
| URL slug (unique per product) | |
| Product name (first row per product) | |
| Description in HTML | |
| Brand or manufacturer | |
| Shopify standard taxonomy | |
| Custom product type | |
| Comma-separated tags | |
| Whether product is visible | or |
Variant columns:
| Column | Description | Example |
|---|---|---|
| First option name | |
| First option value | |
| Second option name | |
| Second option value | |
| Third option name | |
| Third option value | |
| Stock keeping unit | |
| Weight in grams | |
| Stock quantity | |
| Variant price | |
| Original price (for sales) | |
| Physical product | |
| Subject to tax | |
Image columns:
| Column | Description | Example |
|---|---|---|
| Image URL | |
| Display order (1-based) | |
| Alt text for accessibility | |
SEO columns:
| Column | Description | Example |
|---|---|---|
| Page title tag | `Classic T-Shirt |
| Meta description | |
Multi-Variant Row Format
The first row has the product title and details. Subsequent rows for the same product have only the
Handle and variant-specific columns:
Handle,Title,Body (HTML),Vendor,Type,Tags,Published,Option1 Name,Option1 Value,Variant SKU,Variant Price,Variant Inventory Qty,Image Src classic-tshirt,Classic T-Shirt,<p>Premium cotton</p>,My Brand,T-Shirts,"summer,cotton",TRUE,Size,Small,TSH-S,29.95,50,https://example.com/tshirt.jpg classic-tshirt,,,,,,,,Medium,TSH-M,29.95,75, classic-tshirt,,,,,,,,Large,TSH-L,29.95,60,
CSV Rules
- UTF-8 encoding required
- Maximum 50MB file size
- Handle must be unique per product -- duplicate handles update existing products
- Leave variant columns blank on variant rows for fields that don't change
- Images can be on any row -- they're associated by Handle
=Published
makes the product immediately visibleTRUE
Import Steps
- Generate CSV using the column format above
- Use the template from
if availableassets/product-csv-template.csv - Navigate to
https://{store}.myshopify.com/admin/products/import - Upload the CSV file
- Review the preview and confirm import
Use browser automation to assist with the upload if needed.
Step 4: Upload Product Images
Images require a two-step process -- staged upload then attach.
stagedUploadsCreate
mutation stagedUploadsCreate($input: [StagedUploadInput!]!) { stagedUploadsCreate(input: $input) { stagedTargets { url resourceUrl parameters { name value } } userErrors { field message } } }
Input per file:
{ "filename": "product-image.jpg", "mimeType": "image/jpeg", "httpMethod": "POST", "resource": "IMAGE" }
Then upload to the staged URL, and attach with
productCreateMedia:
productCreateMedia
mutation productCreateMedia($productId: ID!, $media: [CreateMediaInput!]!) { productCreateMedia(productId: $productId, media: $media) { media { alt status } mediaUserErrors { field message } } }
Shortcut: If images are already hosted at a public URL, pass
src directly in the product creation:
{ "images": [ { "src": "https://example.com/image.jpg", "alt": "Product front view" } ] }
Step 5: Assign to Collections
collectionAddProducts
mutation collectionAddProducts($id: ID!, $productIds: [ID!]!) { collectionAddProducts(id: $id, productIds: $productIds) { collection { title productsCount } userErrors { field message } } }
To find collection IDs:
{ collections(first: 50) { edges { node { id title handle productsCount } } } }
Step 6: Set Inventory
inventorySetQuantities
mutation inventorySetQuantities($input: InventorySetQuantitiesInput!) { inventorySetQuantities(input: $input) { inventoryAdjustmentGroup { reason } userErrors { field message } } }
Input:
{ "reason": "correction", "name": "available", "quantities": [{ "inventoryItemId": "gid://shopify/InventoryItem/123", "locationId": "gid://shopify/Location/456", "quantity": 50 }] }
To find location IDs:
{ locations(first: 10) { edges { node { id name isActive } } } }
Step 7: Verify
Query back the created products to confirm:
{ products(first: 50) { edges { node { id title handle status productType vendor variants(first: 10) { edges { node { id title price sku inventoryQuantity } } } images(first: 3) { edges { node { url altText } } } } } pageInfo { hasNextPage endCursor } } }
Provide the admin URL for the user to review:
https://{store}.myshopify.com/admin/products
Critical Patterns
Product Status
New products default to
DRAFT. To make them visible:
{ "status": "ACTIVE" }
Always confirm with the user before setting status to
ACTIVE.
Variant Limits
Shopify allows max 100 variants per product and 3 options (e.g. Size, Colour, Material). If you need more, split into separate products.
Price Formatting
Prices are strings, not numbers. Always quote them:
"price": "29.95" not "price": 29.95.
HTML Descriptions
Product descriptions accept HTML. Keep it simple -- Shopify's editor handles basic tags:
,<p>
,<strong>
,<em>
,<ul>
,<ol>
,<li>
-<h2><h6>
for links<a href="...">
is stripped -- use product images instead<img>
Bulk Operations for Large Imports
For 50+ products via API, use Shopify's bulk operation:
mutation { bulkOperationRunMutation( mutation: "mutation ($input: ProductInput!) { productCreate(input: $input) { product { id } userErrors { message } } }" stagedUploadPath: "tmp/bulk-products.jsonl" ) { bulkOperation { id status } userErrors { message } } }
This accepts a JSONL file with one product per line, processed asynchronously.
Asset Files
-- Blank CSV template with Shopify import headersassets/product-csv-template.csv