Loading...
Loading...
Finds expired, zero-usage, or duplicate discount codes and optionally deactivates or deletes them.
npx skill4agent add 40rty-ai/shopify-admin-skills shopify-admin-discount-hygiene-cleanupdiscount-ab-analysisshopify store auth --store <domain> --scopes read_discounts,write_discountsread_discountswrite_discounts| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| store | string | yes | — | Store domain (e.g., mystore.myshopify.com) |
| flag_expired | bool | no | true | Flag/delete discounts past their end date |
| flag_zero_usage | bool | no | true | Flag/delete discounts with 0 redemptions older than N days |
| zero_usage_min_age_days | integer | no | 30 | Age threshold for zero-usage flags |
| dry_run | bool | no | true | Preview without executing mutations |
| format | string | no | human | Output format: |
⚠️permanently removes discount codes. Deleted codes cannot be recovered. Customers who received a deleted code will find it invalid. Run withdiscountCodeDeleteto review the flagged list before committing. Always check that expired codes are not referenced in active email campaigns before deleting.dry_run: true
discountNodesfirst: 250discount { ... on DiscountCodeBasic { codes, usageLimit, asyncUsageCount, endsAt, status } }hasNextPage: falseflag_expiredflag_zero_usagezero_usage_min_age_daysdiscountCodeDeleteid: <discount_node_id>deletedCodeDiscountIduserErrors# discountNodes:query — validated against api_version 2025-01
query DiscountAudit($after: String) {
discountNodes(first: 250, after: $after) {
edges {
node {
id
discount {
... on DiscountCodeBasic {
title
status
createdAt
endsAt
asyncUsageCount
usageLimit
codes(first: 5) {
edges {
node {
id
code
}
}
}
}
... on DiscountCodeBxgy {
title
status
createdAt
endsAt
asyncUsageCount
usageLimit
}
... on DiscountCodeFreeShipping {
title
status
createdAt
endsAt
asyncUsageCount
usageLimit
}
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}# discountCodeDelete:mutation — validated against api_version 2025-01
mutation DiscountCodeDelete($id: ID!) {
discountCodeDelete(id: $id) {
deletedCodeDiscountId
userErrors {
field
message
}
}
}╔══════════════════════════════════════════════╗
║ SKILL: Discount Hygiene Cleanup ║
║ Store: <store domain> ║
║ Started: <YYYY-MM-DD HH:MM UTC> ║
╚══════════════════════════════════════════════╝[N/TOTAL] <QUERY|MUTATION> <OperationName>
→ Params: <brief summary of key inputs>
→ Result: <count or outcome>dry_run: true[DRY RUN]format: human══════════════════════════════════════════════
OUTCOME SUMMARY
Discounts scanned: <n>
Expired: <n>
Zero usage (> <n> days): <n>
Total flagged: <n>
Deleted: <n>
Errors: <n>
Output: discount_cleanup_<date>.csv
══════════════════════════════════════════════format: json{
"skill": "discount-hygiene-cleanup",
"store": "<domain>",
"started_at": "<ISO8601>",
"dry_run": true,
"outcome": {
"scanned": 0,
"flagged_expired": 0,
"flagged_zero_usage": 0,
"deleted": 0,
"errors": 0,
"output_file": "discount_cleanup_<date>.csv"
}
}discount_cleanup_<YYYY-MM-DD>.csvdiscount_idtitlestatuscreated_atends_atusage_countusage_limitflag_reasonaction| Error | Cause | Recovery |
|---|---|---|
| API rate limit exceeded | Wait 2 seconds, retry up to 3 times |
| Discount already deleted or active order using it | Log error, skip, continue |
| No discounts flagged | Clean discount catalog | Exit with ✅ no cleanup needed |
flag_zero_usage