Install
$ agentstack add skill-40rty-ai-shopify-admin-skills-shopify-admin-product-tag-bulk-update ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
About
Purpose
Adds or removes one or more tags across a set of products in bulk — replacing manual product-by-product editing in the Shopify admin. Use for campaign setup (add summer-sale to a collection before launch), campaign teardown (remove flash-sale after it ends), or catalog reorganization (retag products moving between categories). Tags drive collection rules, marketing segments, and reporting filters, so bulk accuracy matters. Replaces manual Shopify admin bulk editing and CSV import/export workflows.
Prerequisites
shopify auth login --store- API scopes:
read_products,write_products
Parameters
Universal (store, format, dry_run) + skill-specific:
| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | action | string | yes | — | add or remove | | tags | array | yes | — | One or more tag strings to add or remove (e.g., ["summer-sale", "clearance"]) | | collectionid | string | no | — | GID of a collection — target all products in this collection | | filtertag | string | no | — | Target all products that currently have this tag | | query_filter | string | no* | — | Shopify product search query (e.g., "product_type:Apparel") |
*One of collection_id, filter_tag, or query_filter is required.
Safety
> ⚠️ Step 2 executes bulk tag mutations. tagsRemove is irreversible — if you remove the wrong tag, you must re-add it manually or run this skill again with action: add. Run with dry_run: true to see the full product list before committing. For large catalogs (1000+ products), dry_run is strongly recommended before any removal operation.
Workflow Steps
- OPERATION:
products— query
Inputs: first: 250, query built from collection_id, filter_tag, or query_filter; paginate until all matching products fetched Expected output: List of product GIDs and current tag arrays; confirm target set before proceeding
- OPERATION:
tagsAdd— mutation (ifaction: add)
Inputs: id: , tags: per product Expected output: Updated node.id with userErrors
OR
- OPERATION:
tagsRemove— mutation (ifaction: remove)
Inputs: id: , tags: per product Expected output: Updated node with userErrors
GraphQL Operations
# products:query — validated against api_version 2025-01
query ProductsForTagUpdate($first: Int!, $after: String, $query: String) {
products(first: $first, after: $after, query: $query) {
edges {
node {
id
title
tags
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
# tagsAdd:mutation — validated against api_version 2025-01
mutation TagsAdd($id: ID!, $tags: [String!]!) {
tagsAdd(id: $id, tags: $tags) {
node {
id
}
userErrors {
field
message
}
}
}
# tagsRemove:mutation — validated against api_version 2025-01
mutation TagsRemove($id: ID!, $tags: [String!]!) {
tagsRemove(id: $id, tags: $tags) {
node {
id
}
userErrors {
field
message
}
}
}
Session Tracking
Claude MUST emit the following output at each stage. This is mandatory.
On start, emit:
╔══════════════════════════════════════════════╗
║ SKILL: product-tag-bulk-update ║
║ Store: ║
║ Started: ║
╚══════════════════════════════════════════════╝
After each step, emit:
[N/TOTAL]
→ Params:
→ Result:
If dry_run: true, prefix every mutation step with [DRY RUN] and do not execute it.
On completion, emit:
For format: human (default):
══════════════════════════════════════════════
OUTCOME SUMMARY
Products targeted:
Tags added/removed:
Action:
Errors:
Output:
══════════════════════════════════════════════
For format: json, emit a JSON object with this schema:
{
"skill": "product-tag-bulk-update",
"store": "",
"started_at": "",
"completed_at": "",
"dry_run": false,
"steps": [
{
"step": 1,
"operation": "",
"type": "query|mutation",
"params_summary": "",
"result_summary": "",
"skipped": false
}
],
"outcome": {
"action": "",
"tags": ["", ""],
"products_targeted": 0,
"mutations_succeeded": 0,
"errors": 0,
"output_file": ""
}
}
Output Format
CSV file tag-update-.csv with columns: product_id, product_title, action, tags_changed, previous_tags, result.
Error Handling
| Error | Cause | Recovery | |-------|-------|----------| | No products returned | Filter matches nothing | Check collection GID or filter_tag spelling | | userErrors from tagsAdd/tagsRemove | Tag string too long or invalid characters | Shopify tag max length is 255 characters; no commas allowed | | Large product count (1000+) | Many API calls needed for pagination | Expected — the skill paginates automatically; may take longer | | Tag not present (on remove) | Product doesn't have the tag you're removing | Silently skips — tagsRemove on a non-existent tag is a no-op |
Best Practices
- Run
dry_run: truebefore anyaction: remove— the CSV preview shows exactly which products will lose the tag. - To set up a campaign cleanly, run
action: addat launch andaction: removeat the end — keeping your product tags tidy prevents collection rule drift. - Use
query_filter: "tag:old-campaign-name"for teardown — it will find exactly the products tagged from the previous run. - You can add multiple tags in one run — pass
tags: ["sale", "homepage-featured", "clearance"]to apply all three atomically. - Tags are case-insensitive in Shopify collection rules but case-preserving in the API — use consistent casing to avoid duplicates like
Saleandsale.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: 40RTY-ai
- Source: 40RTY-ai/shopify-admin-skills
- License: MIT
- Homepage: http://skills.40rty.ai
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.