Agentic Image Alt Text

Generate and set descriptive alt text on product images so AI agents (which can't 'see' pixels) can understand and recommend what each product looks like.

shopify-admin-agentic-image-alt-text


Purpose

An AI shopping agent reads image alt text to understand a product's appearance — color, material, style, use. Blank or filename-style alt text ("IMG_2931.jpg") tells the agent nothing, so it can't match the product to a visual query ("red linen midi dress") or describe it to a shopper. This skill finds product images with missing or low-value alt text and writes concise, descriptive alt text derived from the product's title, options, type, and tags. Fixes the agentiq.report finding listing-image-alt-text.


Prerequisites

  • Authenticated Shopify CLI session (shopify auth login --store )
  • Required API scopes: read_products, write_products, write_files

  • Parameters

    All skills accept these universal parameters:


    ParameterTypeRequiredDefaultDescription
    storestringyesStore domain (e.g., mystore.myshopify.com)
    formatstringnohumanOutput format: human (default) or json
    dry_runboolnofalsePreview mutations without executing

    Skill-specific parameters:


    ParameterTypeRequiredDefaultDescription
    collection_idstringnoLimit to a collection GID
    tagstringnoLimit to a product tag
    overwriteboolnofalseIf true, also rewrite filename-style / placeholder alt text; if false, only fill blanks
    max_charsintno125Alt-text length cap (accessibility + agent-readability)

    Safety


    > ⚠️ Step 3 (fileUpdate) writes alt text to live media. With overwrite: true it replaces existing alt text, which is not bulk-reversible. Run dry_run: true first and review the proposed alt text per image. Default (overwrite: false) only fills blanks and is low-risk.


    Workflow Steps


  • OPERATION: products — query
  • Inputs: first: 250, optional collection/tag filter; fields title, productType, tags, options, media{ MediaImage{ id image{ altText } } }; paginate until done.

    Expected output: Product media with current alt text + the product context needed to generate good alt text.


  • COMPUTE (no API): for each image with blank (or, if overwrite, placeholder) alt text, compose alt text from title + relevant option values (color/material/style) + product type, trimmed to max_chars. Emit a preview table (image id → proposed alt).

  • OPERATION: fileUpdate — mutation
  • Inputs: files: [{ id: , alt: }] in batches (≤ 25).

    Expected output: Updated files; collect userErrors.


    GraphQL Operations


    # products:query — validated against api_version 2025-01
    query AltTextProducts($first: Int!, $after: String, $query: String) {
      products(first: $first, after: $after, query: $query) {
        edges {
          node {
            id
            title
            productType
            tags
            options { name values }
            media(first: 20) {
              edges {
                node { ... on MediaImage { id image { altText url } } }
              }
            }
          }
        }
        pageInfo { hasNextPage endCursor }
      }
    }
    

    # fileUpdate:mutation — validated against api_version 2025-01
    mutation AltTextUpdate($files: [FileUpdateInput!]!) {
      fileUpdate(files: $files) {
        files { ... on MediaImage { id alt } }
        userErrors { field message code }
      }
    }
    

    Session Tracking


    Claude MUST emit the following output at each stage. This is mandatory.


    On start, emit:

    ╔══════════════════════════════════════════════╗
    ║  SKILL: <skill name>                         ║
    ║  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
      <Metric label>:   <value>
      Errors:           0
      Output:           <filename or "none">
    ══════════════════════════════════════════════
    

    For format: json, emit:

    {
      "skill": "<skill-slug>",
      "store": "<domain>",
      "started_at": "<ISO8601>",
      "completed_at": "<ISO8601>",
      "dry_run": false,
      "steps": [
        {
          "step": 1,
          "operation": "<OperationName>",
          "type": "query",
          "params_summary": "<string>",
          "result_summary": "<string>",
          "skipped": false
        }
      ],
      "outcome": {
        "metric_key": 0,
        "errors": 0,
        "output_file": null
      }
    }
    

    Output Format

    human: count of images updated + a CSV (product, image_id, old_alt, new_alt). json: { images_updated, images_skipped, errors, output_file }.


    Error Handling

    ErrorCauseRecovery
    THROTTLEDAPI rate limitWait 2s, retry up to 3 times
    userErrors non-emptyInvalid file id / not a MediaImageLog message, skip, continue
    Alt exceeds limitGenerated text too longTrim to max_chars at a word boundary

    Best Practices

  • Lead the alt text with the concrete noun + distinguishing attribute ("Charcoal merino crew-neck sweater"), not marketing fluff — that's what agents match on.
  • Keep overwrite: false for the first pass to fix the worst gap (blanks) safely; do a reviewed overwrite: true pass later for filename-style alts.
  • Don't keyword-stuff — one accurate sentence beats a comma salad and reads better for screen-reader users too.
  • Re-run shopify-admin-agentic-readiness-audit afterward to confirm the alt-text coverage pillar moved.