shopify-admin-abandoned-cart-recovery

SkillCommerce & finance

Query checkouts abandoned in the last N days, generate unique discount codes per customer, and tag them for re-engagement.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the shopify-admin-abandoned-cart-recovery skill

What this skill tells your AI

The instructions your AI receives, as published by 40rty-ai/shopify-admin-skills in skills/marketing/shopify-admin-abandoned-cart-recovery/SKILL.md and read by ahel’s review.

Purpose

Identifies customers who started checkout but did not complete their purchase, generates a unique discount code for each one, and tags them in Shopify so they can be targeted in follow-up campaigns. This skill handles the Shopify-native data layer (querying, discounts, tagging); sending the actual email requires an external tool.

Prerequisites

  • Authenticated Shopify CLI session: shopify auth login --store <domain>
  • API scopes: read_checkouts, write_price_rules, write_discount_codes, write_customers

Parameters

ParameterTypeRequiredDefaultDescription
storestringyesStore domain (e.g., mystore.myshopify.com)
formatstringnohumanOutput format: human or json
dry_runboolnofalsePreview operations without executing mutations
days_backintegerno7Lookback window for abandoned checkouts
min_cart_valuefloatno0Minimum cart total to include (USD)
discount_pctintegerno10Discount percentage to create per customer
code_prefixstringnoRECOVERPrefix for generated discount codes
tagstringnocart-recoveryTag applied to eligible customers

Safety

⚠️ Steps 2 and 3 execute mutations (discount code creation, customer tagging). Discount codes created via discountCodeBulkCreate cannot be bulk-deleted via the Admin API — they must be removed individually or via the price rule. Run with dry_run: true to verify eligible customer count before committing.

Workflow Steps

  1. OPERATION: abandonedCheckouts — query Inputs: first: 250, query: "created_at:>='<NOW - days_back days>'", pagination cursor Expected output: List of checkout objects with email, totalPrice, createdAt, lineItems; paginate until hasNextPage: false

  2. OPERATION: discountCodeBulkCreate — mutation Inputs: For each eligible customer email: a unique code {code_prefix}-{UUID[:8].toUpperCase()}, percentage discount discount_pct, usage limit 1, expiry 30 days Expected output: Confirmation of code creation with code and priceRule.id; collect userErrors

  3. OPERATION: tagsAdd — mutation Inputs: Customer id (from checkout email lookup), tag string from tag parameter Expected output: Updated customer tags; collect userErrors

GraphQL Operations

# abandonedCheckouts:query — validated against api_version 2025-04
query AbandonedCheckouts($first: Int!, $after: String, $query: String) {
  abandonedCheckouts(first: $first, after: $after, query: $query) {
    edges {
      node {
        id
        customer {
          defaultEmailAddress {
            emailAddress
          }
        }
        totalPriceSet {
          shopMoney {
            amount
            currencyCode
          }
        }
        createdAt
        lineItems {
          edges {
            node {
              title
              quantity
              variant {
                price
              }
            }
          }
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
# discountCodeBulkCreate:mutation — validated against api_version 2025-01
mutation DiscountCodeBulkCreate($priceRuleId: ID!, $codes: [DiscountCodeInput!]!) {
  discountCodeBulkCreate(priceRuleId: $priceRuleId, codes: $codes) {
    bulkCreations {
      id
      done
    }
    userErrors {
      field
      message
    }
  }
}
# tagsAdd:mutation — validated against api_version 2025-01
mutation TagsAdd($id: ID!, $tags: [String!]!) {
  tagsAdd(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: Abandoned Cart Recovery              ║
║  Store: <store domain>                       ║
║  Started: <YYYY-MM-DD HH:MM UTC>             ║
╚══════════════════════════════════════════════╝

After each step, emit:

[N/TOTAL] <QUERY|MUTATION>  <OperationName>
          → Params: <brief summary of key inputs>
          → Result: <count or outcome>

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
  Checkouts found:     <n>
  Eligible customers:  <n>
  Codes created:       <n>
  Customers tagged:    <n>
  Errors:              <n>
  Output:              recovery_list_<date>.csv
══════════════════════════════════════════════

For format: json, emit:

{
  "skill": "abandoned-cart-recovery",
  "store": "<domain>",
  "started_at": "<ISO8601>",
  "completed_at": "<ISO8601>",
  "dry_run": false,
  "steps": [
    { "step": 1, "operation": "AbandonedCheckouts", "type": "query", "params_summary": "last 7 days, cart >= $0", "result_summary": "<n> checkouts", "skipped": false },
    { "step": 2, "operation": "DiscountCodeBulkCreate", "type": "mutation", "params_summary": "<n> codes, 10% off, prefix RECOVER", "result_summary": "<n> created", "skipped": false },
    { "step": 3, "operation": "TagsAdd", "type": "mutation", "params_summary": "tag: cart-recovery", "result_summary": "<n> customers tagged", "skipped": false }
  ],
  "outcome": {
    "checkouts_found": 0,
    "eligible_customers": 0,
    "codes_created": 0,
    "customers_tagged": 0,
    "errors": 0,
    "output_file": "recovery_list_<date>.csv"
  }
}

Output Format

CSV file recovery_list_<YYYY-MM-DD>.csv with columns: customer_email, cart_total, abandoned_at, discount_code, discount_pct, tag_applied

Error Handling

ErrorCauseRecovery
THROTTLEDAPI rate limit exceededWait 2 seconds, retry up to 3 times
userErrors on discount creationInvalid price rule or duplicate codeLog error, skip customer, continue
userErrors on tagsAddCustomer ID not foundLog error, skip tag, continue
No email on checkoutGuest checkout without emailSkip checkout, do not count as eligible

Best Practices

  • Run with dry_run: true first and confirm eligible customer count — discount codes cannot be bulk-deleted via API and will persist in your price rules until manually removed.
  • Set min_cart_value to focus recovery effort on higher-value carts (e.g., 50 for $50+). Small carts often have lower recovery intent and may not justify a discount.
  • Use a unique tag per run (e.g., cart-recovery-2026-04-11) so you can identify exactly which customers were targeted in each campaign and avoid re-targeting them in subsequent runs.
  • The code_prefix should be meaningful to operators reviewing discount codes in Shopify Admin (e.g., CART10 for a 10% cart recovery discount).
  • This skill produces the data layer. Use Shopify Email or another email tool to send the generated codes to customers.

Signals

GitHub stars
187
Forks
18
Last commit
Aug 2026

ahel review

  • S4info
    community integration — published by 40rty-ai, not shopify

Automated review, not a security audit. Ruleset v1.

Advanced
Catalog kind
skill
Gateway key
shopify-admin-abandoned-cart-recovery
Source
github.com/40rty-ai/shopify-admin-skills