49 min readContent generation

Implementation Guide: Generate product descriptions, category pages, and email campaigns at scale

Step-by-step implementation guide for deploying AI to generate product descriptions, category pages, and email campaigns at scale for Retail clients.

Hardware Procurement

Business Workstation (if client needs upgrade)

Business Workstation

Dell or LenovoDell OptiPlex 7020 Tower (i5-14500, 16GB RAM, 512GB SSD) or Lenovo ThinkCentre M80s Gen 5Qty: 1

$800-$1,100 per unit (MSP cost) / $1,200-$1,600 suggested resale — most clients will NOT need this as existing hardware is sufficient

Staff workstation for accessing SaaS AI content platforms via browser. Only procure if client's existing machines are older than 5 years or have less than 8GB RAM. Any modern PC or Mac with a current browser is sufficient.

Business-Grade Router/Firewall (if internet upgrade needed)

Ubiquiti UniFi Dream Machine Pro

UbiquitiUDM-ProQty: 1

$379 MSP cost / $550–$650 suggested resale — only if client lacks reliable 50+ Mbps connectivity

Ensures reliable, low-latency internet connectivity for API calls and SaaS platform access. All AI inference runs in vendor clouds, so stable internet is the single most important infrastructure requirement. Only procure if client's current networking equipment is inadequate.

Software Procurement

Hypotenuse AI Professional

Hypotenuse AISaaS - monthly subscriptionQty: 1

$79/month MSP cost / $119-$149/month suggested resale

Primary AI content generation platform for product descriptions, category pages, and bulk catalog content. Purpose-built for ecommerce with image-to-text capability, brand voice configuration, bulk generation, and direct Shopify/WooCommerce/BigCommerce integrations. SOC 2 Type II certified.

Klaviyo Email Marketing (Standard Plan)

KlaviyoStandard PlanQty: SaaS - monthly subscription based on contact count

Starting at $20/month for 500 contacts (MSP cost) / $40-$60/month suggested resale — scales with contact list size

Email marketing platform with built-in AI content generation for email campaigns, subject lines, and SMS. AI features including predictive analytics and content generation are included at no additional cost. Native integrations with Shopify, WooCommerce, BigCommerce, and 350+ other platforms.

OpenAI API (GPT-4.1-mini)

OpenAIGPT-4.1-mini

~$0.40 per 1M input tokens / ~$1.60 per 1M output tokens. Typical monthly cost for a 5,000 SKU retailer: $5-$25/month actual API cost / Resell as flat $100-$250/month managed service

Foundation model API for custom bulk content generation pipeline. GPT-4.1-mini provides the best cost-to-quality ratio for high-volume product description generation. Used in Tier C custom implementations for automated content workflows.

OpenAI API (GPT-4.1)

OpenAIGPT-4.1

$2.00 per 1M input tokens / $8.00 per 1M output tokens. Used selectively for premium content (category pages, flagship product descriptions).

Higher-quality foundation model for complex content generation tasks such as category landing pages, brand storytelling, and flagship product descriptions that require more nuanced writing quality.

$0 additional cost — included in existing Shopify subscription

Native AI product description generation built directly into Shopify admin. Zero-integration quick win. Generates product descriptions in core supported languages directly from the product editing interface. License type: Included free with paid Shopify plan.

WriteText.ai (if client is on WooCommerce)

WriteText.aiSaaS - monthly or annual subscriptionQty: 110 monthly credits

$9/month or $99/year (MSP cost) / $25-$35/month suggested resale

WordPress/WooCommerce-native plugin for AI product description generation with built-in keyword analysis and SEO optimization. Installs directly inside WooCommerce admin panel. Also supports Magento and Shopify.

Describely (alternative to Hypotenuse AI)

DescribelySaaS - monthly subscription

Starting at $28/month (MSP cost) / $49-$59/month suggested resale

Alternative AI product content platform with strong catalog management features. Used by Target Australia with 98% accuracy rate. Good option for clients with very large catalogs who need enrichment, SEO optimization, and metadata generation in a single tool.

Jasper AI Pro (alternative - marketing-heavy clients)

Jasper AISaaS - per-seat monthly subscriptionQty: per seat

$59/month per seat (MSP cost) / $89-$119/month per seat suggested resale

General AI marketing content platform with strongest brand voice controls and large template library. Best for marketing-heavy retail clients who need diverse content types beyond just product descriptions. LLM-agnostic architecture with enterprise-grade security. Jasper Solutions Partner Program available for MSPs.

Prerequisites

  • Operational ecommerce platform (Shopify, WooCommerce, BigCommerce, or Magento) with admin access credentials
  • Structured product catalog data available — at minimum: SKU, product name, key attributes (size, color, material, etc.), and category assignments. Export capability via CSV or API required.
  • High-quality product images for image-to-text AI features (minimum 800x800px, white background preferred, multiple angles ideal)
  • Active email marketing platform (Klaviyo or Mailchimp) with existing subscriber list and sending domain authenticated (SPF/DKIM/DMARC configured)
  • Reliable internet connectivity at 50+ Mbps with stable uptime
  • Client-designated content approver — a person with authority to review and approve AI-generated content before publication (marketing manager, merchandising lead, or store owner)
  • Documented brand voice guidelines: tone (formal/casual/playful), target audience description, any prohibited words or phrases, competitor differentiation points. If these don't exist, schedule a brand voice workshop as part of the implementation.
  • For WooCommerce: WordPress 6.0+ with PHP 8.0+, WooCommerce 7.0+ installed and active
  • For Shopify: Active paid Shopify plan (Basic, Shopify, or Advanced) — Shopify Magic is not available on development stores
  • For custom API pipeline (Tier C only): Python 3.9+ development environment, Git repository access, cloud hosting account (AWS, GCP, or Azure) for script deployment
  • FTC compliance acknowledgment: client must understand and agree that all AI-generated product content will be reviewed for factual accuracy before publication, and that AI will never be used to generate fake reviews or testimonials

Installation Steps

Step 1: Conduct AI Content Audit and Strategy Session

Before touching any technology, meet with the client's marketing/merchandising team to assess their current content state and define the AI content strategy. Export their full product catalog and analyze: How many products have descriptions? How long/detailed are they? Are they unique or duplicated from manufacturer data? What content types are needed (product descriptions, category pages, email campaigns)? What is the monthly content volume target? Document the client's brand voice by collecting 10-15 examples of content they consider 'on-brand' and 5-10 examples they dislike.

1
Export product catalog from Shopify via CLI (if using Shopify CLI)
2
Or export via Admin: Products > Export > All products as CSV
3
For WooCommerce, use WP-CLI to export the product list
4
Count products missing descriptions: In a spreadsheet, filter the 'description' column for blank/empty cells
Authenticate with Shopify CLI
bash
shopify auth login --store {store-name}.myshopify.com
Export WooCommerce product catalog via WP-CLI
bash
wp wc product list --format=csv --fields=id,name,description,short_description,categories,attributes,images > product_catalog_export.csv --user=1
Note

This step is critical and should not be skipped. The quality of the AI implementation depends entirely on understanding the client's content needs and brand voice. Budget 2-4 hours for this session. Deliverable: AI Content Strategy Document covering content types, volume targets, brand voice guide, and recommended tool stack.

Step 2: Provision and Configure Hypotenuse AI Account

Create the Hypotenuse AI account under the MSP's management. Select the Professional plan ($79/month) which includes 100,000 words per month — sufficient for most SMB retailers. Configure the workspace with the client's brand identity, connect their ecommerce platform, and set up the brand voice profile using the examples collected in Step 1.

1
Navigate to https://www.hypotenuse.ai and create account
2
Select Professional plan ($79/month)
3
In Settings > Brand Voice: Upload 10-15 sample product descriptions that match desired tone; Set brand name, industry (Retail/Ecommerce); Define tone attributes (e.g., 'professional but approachable, confident, not salesy'); Add prohibited words/phrases list
4
In Settings > Integrations: Connect Shopify: Click 'Connect Shopify' > Enter store URL > Authorize; Or connect WooCommerce: Enter store URL + API key + secret; Or connect BigCommerce: Enter store hash + API token
5
In Settings > Templates: Create custom template for 'Product Description' with: Target length: 150-250 words; Required sections: opening hook, key features, use case, specifications; SEO: Include primary keyword in first sentence. Create custom template for 'Category Page' with: Target length: 300-500 words; Required sections: category overview, key benefits, featured products, buying guide
6
Test with 5 sample products to validate output quality
Note

If the client has fewer than 50 products and is on Shopify, consider starting with Shopify Magic (Step 2A) instead of Hypotenuse AI to minimize cost. Hypotenuse AI is recommended when the client has 100+ products or needs bulk generation capabilities. Save the login credentials in the MSP's password manager (e.g., IT Glue, Hudu) under the client's account.

Step 3: Enable Platform-Native AI Features (Quick Wins)

Enable free AI features already built into the client's existing platforms. This provides immediate value and familiarizes the client with AI-generated content before the more sophisticated tools are deployed.

SHOPIFY MAGIC (Shopify stores)

1
Log into Shopify Admin
2
Go to Products > Select any product
3
In the Description field, click the sparkle/AI icon
4
Enter product features/keywords when prompted
5
Review generated description, edit as needed, save
6
Repeat for high-priority products

KLAVIYO AI (Email campaigns)

1
Log into Klaviyo
2
Go to Campaigns > Create Campaign > Email
3
In the email editor, click on a text block
4
Click the AI icon (lightning bolt) to generate: Subject lines (generates 3 variants), Email body copy, Product descriptions within email
5
Configure AI settings: Go to Settings > AI Features > Enable all AI capabilities, Set brand voice preferences in AI settings

MAILCHIMP INTUIT ASSIST (if using Mailchimp)

1
Log into Mailchimp
2
Create new campaign or automation
3
In the email builder, look for 'Intuit Assist' AI prompts
4
Use AI to generate subject lines, body copy, CTAs
5
In Settings > AI Preferences, configure brand tone
Note

These native AI features are free and can be enabled in under an hour. Use them as a 'quick win' demonstration for the client to build confidence in AI-generated content. Document which products/emails were generated with AI vs. written manually for the compliance audit trail.

Step 4: Prepare Product Catalog Data for Bulk Generation

Export the client's full product catalog, clean and structure the data for optimal AI content generation. The quality of input data directly determines the quality of AI output. Create a standardized CSV format that includes all product attributes the AI needs to generate accurate descriptions.

Data Export

1
Shopify CSV Export: Admin > Products > Export > All products > Export as CSV. This gives you: Handle, Title, Body (HTML), Vendor, Type, Tags, Variant SKU, etc.
2
WooCommerce CSV Export via WP-CLI: run the command below
WooCommerce: Export full product catalog via WP-CLI
bash
wp wc product list --format=csv --fields=id,name,slug,type,status,description,short_description,sku,price,regular_price,categories,tags,images,attributes --per_page=100 --user=1 > full_catalog.csv

Data Cleaning

clean_catalog.py
python
# Strip HTML, flag thin content, and export products needing descriptions

# Save as clean_catalog.py
import pandas as pd
import re

df = pd.read_csv('full_catalog.csv')

# Remove HTML tags from existing descriptions
df['clean_description'] = df['description'].apply(lambda x: re.sub('<[^<]+?>', '', str(x)) if pd.notna(x) else '')

# Flag products with missing or thin descriptions (< 50 words)
df['word_count'] = df['clean_description'].apply(lambda x: len(str(x).split()))
df['needs_content'] = df['word_count'] < 50

# Summary
print(f'Total products: {len(df)}')
print(f'Products needing content: {df["needs_content"].sum()}')
print(f'Products with adequate content: {(~df["needs_content"]).sum()}')

# Export products needing content
df[df['needs_content']].to_csv('products_need_content.csv', index=False)
print(f'Exported {df["needs_content"].sum()} products to products_need_content.csv')

Data Enrichment

Ensure each product row has all required and recommended fields before passing to the AI generation pipeline.

Note

Data quality is the #1 factor in AI content quality. Spend adequate time on this step. Common issues to watch for: duplicate SKUs, inconsistent category naming, missing attributes, manufacturer descriptions in foreign languages. For clients with a PIM (Product Information Management) system like Akeneo or Salsify, export from the PIM rather than the ecommerce platform for richer attribute data.

Step 5: Execute Bulk Product Description Generation

Using Hypotenuse AI's bulk generation feature, process the cleaned product catalog to generate descriptions for all products that need content. Start with a test batch of 25-50 products, review quality with the client, adjust brand voice settings if needed, then process the full catalog.

OPTION A: Hypotenuse AI Bulk Generation (UI-based)

1
Log into Hypotenuse AI
2
Go to Batch/Bulk Generation
3
Upload the cleaned CSV (products_need_content.csv)
4
Map columns: product_name, category, attributes, image_urls
5
Select your configured product description template
6
Start with TEST BATCH: Select 25-50 products > Generate
7
Review test batch output with client content approver
8
Adjust brand voice / template settings based on feedback
9
Process FULL BATCH after approval
10
Export generated descriptions as CSV
11
Download and archive the output CSV

OPTION B: Custom API Pipeline (for 5,000+ SKUs)

See Custom AI Components section for full implementation. This option uses OpenAI GPT-4.1-mini for maximum cost efficiency.

Import Generated Content

  • Shopify: Use Shopify CSV import or Matrixify app for bulk update — Admin > Products > Import > Upload CSV with updated descriptions
  • BigCommerce: Use BigCommerce CSV import or API — V2/V3 Catalog API: PUT /catalog/products/{product_id}

WooCommerce: Use WP All Import or WP-CLI

WooCommerce WP-CLI command to bulk update product descriptions
bash
wp wc product update {product_id} --description='Generated description here' --user=1
Critical

CRITICAL: Never auto-publish AI-generated content without human review. Always use a staging/draft workflow where content goes through the client's designated approver. For the test batch, ask the client to rate each description on a 1-5 scale for accuracy, brand voice match, and completeness. Target an average score of 4.0+ before processing the full catalog. Keep a log of all generated content with timestamps for compliance audit trail.

Step 6: Configure Category Page Content Generation

Set up AI generation for category/collection landing pages. These are longer-form content pieces (300-500 words) that describe product categories, include buying guides, and target SEO keywords. Category pages require GPT-4.1 (full model) rather than mini for better quality on longer-form content.

Hypotenuse AI Category Page Generation

1
In Hypotenuse AI, create a new template: 'Category Landing Page'
2
Configure template fields: Category name, Number of products in category, Top-selling products (3-5 product names), Target SEO keyword, Target audience
3
Set output format: H1: Category name + value proposition | Intro paragraph: What this category is, who it's for (100 words) | Key benefits section: 3-4 bullet points with descriptions | Buying guide section: How to choose the right product (150 words) | Featured products mention (50 words) | SEO-optimized meta description (155 characters)
4
Generate for all active categories
5
Review and approve each category page individually
Note

For custom API approach, use GPT-4.1 (not mini) for category pages. See Custom AI Components section for the category page prompt template.

Note

Category pages are client-facing SEO assets that drive organic traffic. They require more careful review than individual product descriptions. Recommend the client's marketing team reviews each category page and potentially edits for unique brand insights. Generate meta titles (60 chars) and meta descriptions (155 chars) alongside the page content.

Step 7: Set Up AI-Powered Email Campaign Workflows

Configure Klaviyo (or Mailchimp) with AI-powered email campaign templates and automation flows. Set up reusable templates for common retail email types: new product announcements, promotional campaigns, abandoned cart sequences, and post-purchase follow-ups. Configure AI to generate subject lines, preview text, and email body copy.

Klaviyo Setup

Abandoned Cart Flow with AI

1
Log into Klaviyo > Flows (for automations)
2
Create Flow > Browse Prebuilt > Abandoned Cart
3
Edit each email in the flow
4
Use AI to generate: subject lines (3 variants for A/B testing), email body with dynamic product blocks, and urgency-driven CTA copy

Welcome Series with AI

1
Create Flow > Browse Prebuilt > Welcome Series
2
Email 1: Brand story (AI-generated, heavily reviewed)
3
Email 2: Best sellers showcase (AI product descriptions)
4
Email 3: First purchase incentive (AI-generated promo copy)

Product Launch Campaign Template

1
Navigate to Campaigns > Create Campaign > Email
2
Design template with: AI-generated hero copy (product value proposition), AI-generated product descriptions (3–4 featured items), and AI-generated CTA variations
3
Save as reusable template

AI Subject Line A/B Testing

1
When creating any campaign, use Klaviyo AI to generate 3+ subject lines
2
Set up A/B test with 20% test audience
3
Auto-send winner to remaining 80%

Mailchimp Setup (if applicable)

1
Log into Mailchimp
2
Create Campaign > Regular Email
3
In the editor, use Intuit Assist for: subject line generation, body copy generation, and product description blocks
4
Save templates for reuse
5
Set up automations with AI-generated content
Note

Email campaigns are where AI content generation delivers the fastest ROI — clients can increase campaign frequency from monthly to weekly without additional writing effort. Always A/B test AI-generated subject lines against each other. Ensure CAN-SPAM compliance: all emails must have unsubscribe links, physical mailing address, and accurate 'From' information regardless of whether content is AI-generated. For GDPR compliance with EU subscribers, ensure consent records are maintained in Klaviyo/Mailchimp.

Step 8: Implement Human-in-the-Loop Review Workflow

Establish a formal content review and approval process that ensures all AI-generated content is verified for accuracy, brand alignment, and regulatory compliance before publication. This is both an FTC compliance requirement and a quality assurance best practice.

Option A: Use Hypotenuse AI's Built-In Review Features

1
In Hypotenuse AI > Settings > Workflow
2
Enable 'Review Required Before Export'
3
Set reviewer to client's content approver email
4
Configure review checklist

Option B: Simple Spreadsheet-Based Review (for Smaller Catalogs)

1
Export AI-generated content to Google Sheets
2
Add columns: 'Reviewer', 'Status' (Pending/Approved/Needs Edit), 'Notes'
3
Share with client approver
4
Only import 'Approved' rows back to ecommerce platform

Option C: Custom API Pipeline

For custom API pipeline, build approval into the workflow. See Custom AI Components section for review dashboard specification.

Compliance Audit Log

Maintain a log (spreadsheet or database) with the following fields:

  • Product SKU
  • Content generation date
  • AI tool/model used
  • Reviewer name
  • Approval date
  • Published date
  • Any edits made by reviewer
Note

FTC compliance is non-negotiable. The biggest risk areas are: (1) AI hallucinating product specifications (e.g., wrong dimensions, incorrect materials), (2) AI generating unsubstantiated health or safety claims, and (3) AI generating content that could be mistaken for customer reviews. Train the client's reviewer to specifically check for these issues. For EU-facing retailers, consider adding AI-generated content disclosures per EU AI Act Article 50 transparency requirements.

Step 9: Configure SEO Optimization for AI-Generated Content

Ensure all AI-generated product descriptions and category pages are optimized for search engines. Configure the AI tools to include target keywords naturally, generate unique meta titles and descriptions, and avoid duplicate content issues that can harm SEO rankings.

SEO Configuration in Hypotenuse AI

1
In template settings, enable 'SEO Optimization'
2
For each product/category, specify: Primary keyword (e.g., 'women's running shoes') and Secondary keywords (e.g., 'lightweight sneakers', 'athletic footwear')
3
Set rules: Include primary keyword in first 50 words, Include primary keyword in generated meta description, Keep meta title under 60 characters, Keep meta description under 155 characters

Duplicate Content Check

After bulk generation, check for duplicate/near-duplicate content:

Install Python dependencies
bash
pip install scikit-learn pandas
Save as check_duplicates.py
python
# detects near-duplicate generated descriptions using TF-IDF cosine
# similarity

import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

df = pd.read_csv('generated_descriptions.csv')

vectorizer = TfidfVectorizer(stop_words='english')
tfidf_matrix = vectorizer.fit_transform(df['generated_description'])

# Find pairs with >85% similarity
similarity_matrix = cosine_similarity(tfidf_matrix)
duplicates = []
for i in range(len(similarity_matrix)):
    for j in range(i+1, len(similarity_matrix)):
        if similarity_matrix[i][j] > 0.85:
            duplicates.append({
                'product_1': df.iloc[i]['product_name'],
                'product_2': df.iloc[j]['product_name'],
                'similarity': round(similarity_matrix[i][j], 3)
            })

dup_df = pd.DataFrame(duplicates)
print(f'Found {len(dup_df)} near-duplicate pairs')
dup_df.to_csv('duplicate_content_flags.csv', index=False)

WriteText.ai SEO (WooCommerce)

1
Install WriteText.ai plugin in WordPress
2
Go to WooCommerce > Products > Edit Product
3
Use WriteText.ai panel to: Auto-analyze target keywords, Generate SEO-optimized descriptions, Auto-generate meta title and description for Yoast/Rank Math
Note

SEO is a critical quality check for AI-generated content. Common AI pitfalls include: keyword stuffing (using the target keyword too many times), generating generic descriptions that are too similar across products in the same category, and creating thin content that doesn't provide enough value. The duplicate content checker should be run after every bulk generation batch. Flag any pairs with >85% similarity for regeneration with more specific prompts.

Step 10: Deploy and Verify in Production

Import all approved AI-generated content into the live ecommerce platform and email marketing system. Verify that content displays correctly across all channels (website, mobile, marketplace listings) and that email campaigns render properly across email clients.

Shopify Import — Option A: Native CSV Import

1
Admin > Products > Import
2
Upload CSV with updated descriptions (must include Handle column to match)
3
Select 'Overwrite existing products that have the same handle'
4
Review preview > Import

Shopify Import — Option B: Matrixify App (for large catalogs)

1
Install Matrixify from Shopify App Store ($20/mo)
2
Import > Upload CSV > Map columns
3
Set to 'Update existing' mode
4
Run import in batches of 500

WooCommerce Import — Option A: WP All Import Pro

1
All Import > New Import > Upload CSV
2
Map description field to product description
3
Set Unique Identifier to SKU
4
Run import

WooCommerce Import — Option B: WP-CLI Batch Update Script

Save as import_descriptions.sh

WP-CLI batch script to update WooCommerce product descriptions from a CSV file
bash
#!/bin/bash
while IFS=, read -r product_id description; do
    wp wc product update $product_id --description="$description" --user=1
    echo "Updated product $product_id"
done < approved_descriptions.csv

Verification Checklist

Note

IMPORTANT: Take a full backup of the product catalog before importing AI-generated content. In Shopify, this means exporting the current CSV as a backup. In WooCommerce, run a full database backup (wp db export backup_before_ai_content.sql). If anything goes wrong, you can restore immediately. Recommend importing during off-peak hours (early morning or late evening) to minimize any brief display issues.

WooCommerce: export full database backup before importing AI-generated content
bash
wp db export backup_before_ai_content.sql

Custom AI Components

Retail Product Description Generator Prompt

Type: prompt A carefully engineered prompt template for generating retail product descriptions via OpenAI or Anthropic APIs. This prompt incorporates brand voice, SEO keywords, product attributes, and output formatting requirements. It is the core prompt used in both manual API calls and the automated bulk generation pipeline.

Implementation

Retail Product Description Generator
python
# system prompt, user prompt template, and OpenAI API integration

# Retail Product Description Generator - System Prompt

SYSTEM_PROMPT = """
You are an expert retail copywriter who creates compelling, accurate product descriptions for an online store.

## Brand Voice
{brand_voice_description}

## Rules
1. ACCURACY IS PARAMOUNT: Only describe features, materials, dimensions, and specifications that are explicitly provided in the product data. Never invent or assume specifications.
2. Include the primary SEO keyword naturally within the first 50 words.
3. Write in second person ('you') to speak directly to the customer.
4. Lead with the primary benefit or use case, not technical specs.
5. Include 3-5 key features as scannable bullet points.
6. End with a subtle call-to-action that encourages purchase.
7. Keep total length between {min_words} and {max_words} words.
8. Do NOT include pricing information unless explicitly instructed.
9. Do NOT make health, safety, or performance claims that aren't supported by the provided data.
10. Do NOT mention competitor products by name.
11. Generate a meta title (max 60 characters) and meta description (max 155 characters) optimized for the primary keyword.

## Output Format
Return a JSON object with these fields:
- "description_html": The product description in clean HTML (use <p>, <ul>, <li>, <strong> tags only)
- "short_description": A 1-2 sentence summary (max 50 words)
- "meta_title": SEO meta title (max 60 characters)
- "meta_description": SEO meta description (max 155 characters)
- "seo_keywords_used": List of keywords naturally incorporated
"""

USER_PROMPT_TEMPLATE = """
Generate a product description for the following item:

**Product Name:** {product_name}
**SKU:** {sku}
**Category:** {category}
**Primary SEO Keyword:** {primary_keyword}
**Secondary Keywords:** {secondary_keywords}
**Price:** {price}
**Key Attributes:**
{attributes}
**Additional Notes:** {notes}

Generate the product description following all rules in your instructions. Return valid JSON only.
"""

# Example usage with OpenAI API:
import openai
import json

def generate_product_description(
    product_data: dict,
    brand_voice: str,
    min_words: int = 150,
    max_words: int = 250,
    model: str = "gpt-4.1-mini"
) -> dict:
    """
    Generate a product description using OpenAI API.
    
    Args:
        product_data: Dict with keys: product_name, sku, category, primary_keyword,
                      secondary_keywords, price, attributes, notes
        brand_voice: String describing the brand's tone and style
        min_words: Minimum word count for description
        max_words: Maximum word count for description
        model: OpenAI model to use
    
    Returns:
        Dict with description_html, short_description, meta_title, meta_description
    """
    system = SYSTEM_PROMPT.format(
        brand_voice_description=brand_voice,
        min_words=min_words,
        max_words=max_words
    )
    
    # Format attributes as bullet points
    attributes_text = "\n".join(
        f"- {k}: {v}" for k, v in product_data.get('attributes', {}).items()
    )
    
    user_message = USER_PROMPT_TEMPLATE.format(
        product_name=product_data['product_name'],
        sku=product_data['sku'],
        category=product_data['category'],
        primary_keyword=product_data.get('primary_keyword', product_data['product_name']),
        secondary_keywords=', '.join(product_data.get('secondary_keywords', [])),
        price=product_data.get('price', 'Not specified'),
        attributes=attributes_text,
        notes=product_data.get('notes', 'None')
    )
    
    client = openai.OpenAI()  # Uses OPENAI_API_KEY env var
    
    response = client.chat.completions.create(
        model=model,
        messages=[
            {"role": "system", "content": system},
            {"role": "user", "content": user_message}
        ],
        response_format={"type": "json_object"},
        temperature=0.7,
        max_tokens=1000
    )
    
    result = json.loads(response.choices[0].message.content)
    return result

Bulk Product Content Pipeline

Type: workflow An automated Python pipeline that reads a product catalog CSV, generates descriptions for all products via the OpenAI API with rate limiting and error handling, saves results to a review spreadsheet, and optionally imports approved content back to the ecommerce platform. This is the Tier C custom implementation for clients with 5,000+ SKUs.

Implementation

Bulk Product Content Generation Pipeline
python
#!/usr/bin/env python3
"""
Bulk Product Content Generation Pipeline
Usage: python bulk_generate.py --input catalog.csv --output generated_content.csv --model gpt-4.1-mini
"""

import argparse
import csv
import json
import os
import time
import logging
from datetime import datetime
from pathlib import Path

import openai

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(f'bulk_generate_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

# Rate limiting configuration
RATE_LIMIT_RPM = 500  # Requests per minute for GPT-4.1-mini (Tier 1)
RATE_LIMIT_DELAY = 60.0 / RATE_LIMIT_RPM  # Seconds between requests
MAX_RETRIES = 3
RETRY_DELAY = 5  # Seconds

# Brand voice configuration (customize per client)
BRAND_VOICE = """
Our brand voice is warm, knowledgeable, and approachable. We speak like a trusted friend
who happens to be an expert. We use conversational language but maintain professionalism.
We focus on how products improve our customers' lives, not just features.
We avoid jargon, hyperbole, and aggressive sales language.
"""

SYSTEM_PROMPT = """You are an expert retail copywriter. Generate a product description following these rules:
1. Only describe features explicitly provided in the product data. Never invent specifications.
2. Include the primary SEO keyword naturally within the first 50 words.
3. Write in second person ('you').
4. Lead with the primary benefit, not technical specs.
5. Include 3-5 key features as bullet points.
6. End with a subtle call-to-action.
7. Keep between 150-250 words.
8. No health/safety claims unless supported by data.
9. No competitor mentions.

Brand voice: {brand_voice}

Return a JSON object with: description_html, short_description (max 50 words), meta_title (max 60 chars), meta_description (max 155 chars)"""


def generate_single_description(client: openai.OpenAI, product: dict, model: str) -> dict:
    """Generate a description for a single product with retry logic."""
    user_message = f"""Product: {product.get('product_name', 'Unknown')}
SKU: {product.get('sku', 'N/A')}
Category: {product.get('category', 'General')}
SEO Keyword: {product.get('primary_keyword', product.get('product_name', ''))}
Price: {product.get('price', 'Not specified')}
Attributes: {product.get('attributes', 'None provided')}
Image URL: {product.get('image_url', 'None')}

Generate the product description. Return valid JSON only."""

    for attempt in range(MAX_RETRIES):
        try:
            response = client.chat.completions.create(
                model=model,
                messages=[
                    {"role": "system", "content": SYSTEM_PROMPT.format(brand_voice=BRAND_VOICE)},
                    {"role": "user", "content": user_message}
                ],
                response_format={"type": "json_object"},
                temperature=0.7,
                max_tokens=1000
            )
            
            result = json.loads(response.choices[0].message.content)
            result['tokens_used'] = response.usage.total_tokens
            result['model'] = model
            result['generated_at'] = datetime.now().isoformat()
            result['status'] = 'generated'
            return result
            
        except openai.RateLimitError:
            logger.warning(f"Rate limited on {product.get('sku')}. Waiting 60s...")
            time.sleep(60)
        except openai.APIError as e:
            logger.error(f"API error on {product.get('sku')}: {e}. Attempt {attempt+1}/{MAX_RETRIES}")
            time.sleep(RETRY_DELAY * (attempt + 1))
        except json.JSONDecodeError:
            logger.error(f"Invalid JSON response for {product.get('sku')}. Attempt {attempt+1}/{MAX_RETRIES}")
            time.sleep(RETRY_DELAY)
    
    return {'status': 'failed', 'error': f'Failed after {MAX_RETRIES} attempts', 'sku': product.get('sku')}


def process_catalog(input_file: str, output_file: str, model: str, batch_size: int = 100):
    """Process entire product catalog with progress tracking and checkpointing."""
    client = openai.OpenAI()  # Uses OPENAI_API_KEY env var
    
    # Read input catalog
    with open(input_file, 'r', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        products = list(reader)
    
    logger.info(f"Loaded {len(products)} products from {input_file}")
    
    # Check for existing checkpoint
    checkpoint_file = Path(output_file).with_suffix('.checkpoint.json')
    completed_skus = set()
    if checkpoint_file.exists():
        with open(checkpoint_file, 'r') as f:
            completed_skus = set(json.load(f))
        logger.info(f"Resuming from checkpoint. {len(completed_skus)} already completed.")
    
    # Process products
    results = []
    total_tokens = 0
    failed_count = 0
    
    for i, product in enumerate(products):
        sku = product.get('sku', f'row_{i}')
        
        # Skip if already completed
        if sku in completed_skus:
            continue
        
        logger.info(f"Processing {i+1}/{len(products)}: {product.get('product_name', 'Unknown')} ({sku})")
        
        result = generate_single_description(client, product, model)
        result['sku'] = sku
        result['product_name'] = product.get('product_name', '')
        results.append(result)
        
        if result.get('status') == 'generated':
            total_tokens += result.get('tokens_used', 0)
            completed_skus.add(sku)
        else:
            failed_count += 1
        
        # Rate limiting
        time.sleep(RATE_LIMIT_DELAY)
        
        # Checkpoint every batch_size products
        if (i + 1) % batch_size == 0:
            with open(checkpoint_file, 'w') as f:
                json.dump(list(completed_skus), f)
            logger.info(f"Checkpoint saved. {len(completed_skus)} completed, {failed_count} failed.")
            
            # Write intermediate results
            write_results(results, output_file)
    
    # Final write
    write_results(results, output_file)
    
    # Cost estimate
    est_cost = (total_tokens / 1_000_000) * 2.0  # Approximate blended rate for gpt-4.1-mini
    logger.info(f"\n=== COMPLETE ===")
    logger.info(f"Total products processed: {len(results)}")
    logger.info(f"Successful: {len(results) - failed_count}")
    logger.info(f"Failed: {failed_count}")
    logger.info(f"Total tokens used: {total_tokens:,}")
    logger.info(f"Estimated API cost: ${est_cost:.2f}")
    logger.info(f"Output saved to: {output_file}")


def write_results(results: list, output_file: str):
    """Write results to CSV for review."""
    fieldnames = ['sku', 'product_name', 'status', 'description_html', 'short_description',
                  'meta_title', 'meta_description', 'tokens_used', 'model', 'generated_at',
                  'reviewer', 'approved', 'notes']
    
    with open(output_file, 'w', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction='ignore')
        writer.writeheader()
        writer.writerows(results)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Bulk product description generator')
    parser.add_argument('--input', required=True, help='Input CSV file path')
    parser.add_argument('--output', default='generated_content.csv', help='Output CSV file path')
    parser.add_argument('--model', default='gpt-4.1-mini', help='OpenAI model to use')
    parser.add_argument('--batch-size', type=int, default=100, help='Checkpoint frequency')
    args = parser.parse_args()
    
    process_catalog(args.input, args.output, args.model, args.batch_size)

Category Page Content Generator Prompt

Type: prompt A specialized prompt template for generating longer-form category/collection landing page content. Uses GPT-4.1 (full model) for higher quality on these SEO-critical pages. Generates structured content including intro copy, buying guide, and featured product highlights.

Implementation

System prompt and user template for category page content generation
python
CATEGORY_PAGE_SYSTEM_PROMPT = """
You are an expert ecommerce content strategist and SEO copywriter. You create category landing pages
that serve dual purposes: (1) help shoppers find and choose the right products, and (2) rank well in
search engines for category-level keywords.

Brand voice: {brand_voice}

Rules:
1. Write between 300-500 words total.
2. Use the primary keyword in the H1, first paragraph, and at least one subheading.
3. Include secondary keywords naturally throughout.
4. Structure content with clear HTML headings (H1, H2) and paragraphs.
5. Include a 'How to Choose' or buying guide section with practical advice.
6. Mention 2-3 specific products from the category by name (provided in the data).
7. Write for the target audience specified.
8. Do NOT make unsubstantiated claims.
9. Include internal linking placeholders as [LINK: product-name] that can be replaced with actual URLs.

Return a JSON object with:
- "page_html": Full page content in clean HTML (H1, H2, p, ul, li, strong tags)
- "meta_title": SEO meta title (max 60 characters, include primary keyword)
- "meta_description": SEO meta description (max 155 characters, include primary keyword and CTA)
- "h1_tag": The recommended H1 heading text
- "word_count": Approximate word count of the page content
"""

CATEGORY_PAGE_USER_TEMPLATE = """
Generate a category landing page for:

**Category Name:** {category_name}
**Primary SEO Keyword:** {primary_keyword}
**Secondary Keywords:** {secondary_keywords}
**Number of Products:** {product_count}
**Price Range:** {price_range}
**Top Selling Products:** {top_products}
**Target Audience:** {target_audience}
**Season/Context:** {season_context}

Generate the full category page content. Return valid JSON only.
"""

# Usage: Call with model='gpt-4.1' for category pages (higher quality than mini)
# Temperature: 0.6 (slightly lower than product descriptions for more consistent structure)
# Max tokens: 2000

Email Campaign Content Generator

Type: prompt A prompt template for generating email marketing campaign content including subject lines, preview text, email body copy, and CTAs. Designed to work with Klaviyo or Mailchimp's API for programmatic email content generation. Generates multiple subject line variants for A/B testing.

Implementation:

System and user prompt templates for AI email campaign generation
python
EMAIL_CAMPAIGN_SYSTEM_PROMPT = """
You are an expert email marketing copywriter for a retail brand. You create high-converting
email campaigns that drive opens, clicks, and purchases.

Brand voice: {brand_voice}

Rules:
1. Generate exactly 3 subject line variants for A/B testing (max 50 characters each).
2. Generate preview text (max 90 characters) that complements the subject line.
3. Email body should be concise — max 200 words. Retail emails should be scannable.
4. Include clear, action-oriented CTA text (e.g., 'Shop Now', 'See the Collection').
5. Use urgency or exclusivity sparingly and only when the campaign data warrants it.
6. Personalization tokens: use {{first_name}} for recipient name, {{product_name}} for dynamic product.
7. Do NOT make false urgency claims (e.g., don't say 'limited time' unless it actually is).
8. Include accessibility-friendly alt text suggestions for any image sections.
9. CAN-SPAM compliant: no deceptive subject lines.

Return a JSON object with:
- "subject_lines": Array of 3 subject line strings
- "preview_text": Preview/preheader text string
- "email_body_html": Email body in simple HTML (p, strong, a tags)
- "cta_text": Primary CTA button text
- "cta_url_placeholder": Suggested URL path (e.g., '/collections/summer-sale')
- "alt_texts": Array of suggested alt texts for image placeholders
"""

EMAIL_CAMPAIGN_USER_TEMPLATE = """
Generate email campaign content for:

**Campaign Type:** {campaign_type}  (e.g., 'New Product Launch', 'Seasonal Sale', 'Abandoned Cart', 'Post-Purchase', 'Weekly Newsletter')
**Campaign Name:** {campaign_name}
**Products Featured:** {featured_products}
**Offer/Promotion:** {offer_details}
**Target Segment:** {target_segment}
**Key Message:** {key_message}
**Urgency Level:** {urgency} (none / low / medium / high)
**Season/Context:** {season_context}

Generate the email campaign content. Return valid JSON only.
"""
Klaviyo API integration
python
# create a campaign using AI-generated content

import requests

def create_klaviyo_campaign_with_ai_content(
    klaviyo_api_key: str,
    list_id: str,
    ai_content: dict,
    from_email: str,
    from_name: str
):
    """
    Create a Klaviyo campaign using AI-generated content.
    
    Prerequisites:
    - Klaviyo API key with campaign:write scope
    - Template already created in Klaviyo with content blocks
    """
    headers = {
        'Authorization': f'Klaviyo-API-Key {klaviyo_api_key}',
        'Content-Type': 'application/json',
        'revision': '2024-10-15'
    }
    
    # Create campaign
    campaign_payload = {
        'data': {
            'type': 'campaign',
            'attributes': {
                'name': f'AI Generated - {ai_content.get("campaign_name", "Campaign")}',
                'audiences': {
                    'included': [{'type': 'list', 'id': list_id}]
                },
                'send_strategy': {
                    'method': 'static',
                    'options_static': {'send_time': None}  # Set send time as needed
                },
                'channel': 'email'
            }
        }
    }
    
    response = requests.post(
        'https://a.klaviyo.com/api/campaigns/',
        headers=headers,
        json=campaign_payload
    )
    
    if response.status_code == 201:
        campaign_id = response.json()['data']['id']
        print(f'Campaign created: {campaign_id}')
        print(f'Subject line options: {ai_content["subject_lines"]}')
        print(f'Preview text: {ai_content["preview_text"]}')
        return campaign_id
    else:
        print(f'Error: {response.status_code} - {response.text}')
        return None

Shopify Content Sync Integration

Type: integration

A Python integration that reads approved AI-generated content from the review CSV and pushes it to a Shopify store via the Shopify Admin API. Handles batch updates with rate limiting, error handling, and rollback capability. Supports both product descriptions and collection/category page content.

Implementation:

shopify_sync.py — Shopify Content Sync Integration
python
#!/usr/bin/env python3
"""
Shopify Content Sync Integration
Pushes approved AI-generated content to Shopify via Admin API.

Requirements:
    pip install shopify requests python-dotenv

Environment variables (.env):
    SHOPIFY_STORE_URL=your-store.myshopify.com
    SHOPIFY_ACCESS_TOKEN=shpat_xxxxxxxxxxxxx

Usage:
    python shopify_sync.py --input approved_content.csv --dry-run
    python shopify_sync.py --input approved_content.csv --execute
"""

import argparse
import csv
import json
import os
import time
import logging
from datetime import datetime
from pathlib import Path

import requests
from dotenv import load_dotenv

load_dotenv()
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# Shopify API rate limit: 2 requests/second for standard plans
SHOPIFY_RATE_LIMIT_DELAY = 0.55  # seconds between requests


class ShopifyContentSync:
    def __init__(self, store_url: str, access_token: str):
        self.base_url = f'https://{store_url}/admin/api/2024-10'
        self.headers = {
            'X-Shopify-Access-Token': access_token,
            'Content-Type': 'application/json'
        }
        self.backup = []  # Store original content for rollback
    
    def get_product_by_sku(self, sku: str) -> dict | None:
        """Find a Shopify product by SKU via variant lookup."""
        resp = requests.get(
            f'{self.base_url}/variants.json?sku={sku}&fields=id,product_id,sku',
            headers=self.headers
        )
        time.sleep(SHOPIFY_RATE_LIMIT_DELAY)
        
        if resp.status_code == 200 and resp.json().get('variants'):
            product_id = resp.json()['variants'][0]['product_id']
            product_resp = requests.get(
                f'{self.base_url}/products/{product_id}.json',
                headers=self.headers
            )
            time.sleep(SHOPIFY_RATE_LIMIT_DELAY)
            if product_resp.status_code == 200:
                return product_resp.json().get('product')
        return None
    
    def update_product_content(self, product_id: int, content: dict, dry_run: bool = True) -> bool:
        """Update product description, meta title, and meta description."""
        payload = {
            'product': {
                'id': product_id,
                'body_html': content.get('description_html', ''),
                'metafields_global_title_tag': content.get('meta_title', ''),
                'metafields_global_description_tag': content.get('meta_description', '')
            }
        }
        
        if dry_run:
            logger.info(f'  [DRY RUN] Would update product {product_id}')
            return True
        
        resp = requests.put(
            f'{self.base_url}/products/{product_id}.json',
            headers=self.headers,
            json=payload
        )
        time.sleep(SHOPIFY_RATE_LIMIT_DELAY)
        
        if resp.status_code == 200:
            logger.info(f'  Updated product {product_id} successfully')
            return True
        else:
            logger.error(f'  Failed to update product {product_id}: {resp.status_code} {resp.text}')
            return False
    
    def backup_product(self, product: dict):
        """Save original product content for rollback."""
        self.backup.append({
            'product_id': product['id'],
            'body_html': product.get('body_html', ''),
            'title': product.get('title', ''),
            'backed_up_at': datetime.now().isoformat()
        })
    
    def save_backup(self, filepath: str):
        """Save backup to JSON file."""
        with open(filepath, 'w') as f:
            json.dump(self.backup, f, indent=2)
        logger.info(f'Backup saved to {filepath} ({len(self.backup)} products)')
    
    def rollback(self, backup_filepath: str):
        """Restore original content from backup."""
        with open(backup_filepath, 'r') as f:
            backup_data = json.load(f)
        
        for item in backup_data:
            payload = {'product': {'id': item['product_id'], 'body_html': item['body_html']}}
            resp = requests.put(
                f'{self.base_url}/products/{item["product_id"]}.json',
                headers=self.headers,
                json=payload
            )
            time.sleep(SHOPIFY_RATE_LIMIT_DELAY)
            if resp.status_code == 200:
                logger.info(f'Rolled back product {item["product_id"]}')
            else:
                logger.error(f'Failed to rollback product {item["product_id"]}')


def sync_approved_content(input_file: str, dry_run: bool = True):
    """Main sync function: reads approved CSV and pushes to Shopify."""
    store_url = os.getenv('SHOPIFY_STORE_URL')
    access_token = os.getenv('SHOPIFY_ACCESS_TOKEN')
    
    if not store_url or not access_token:
        raise ValueError('Set SHOPIFY_STORE_URL and SHOPIFY_ACCESS_TOKEN in .env')
    
    sync = ShopifyContentSync(store_url, access_token)
    
    with open(input_file, 'r', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        rows = [r for r in reader if r.get('approved', '').lower() == 'yes']
    
    logger.info(f'Found {len(rows)} approved products to sync')
    
    success = 0
    failed = 0
    
    for row in rows:
        sku = row.get('sku', '')
        logger.info(f'Processing SKU: {sku} - {row.get("product_name", "")}')
        
        product = sync.get_product_by_sku(sku)
        if not product:
            logger.warning(f'  Product not found for SKU: {sku}')
            failed += 1
            continue
        
        sync.backup_product(product)
        
        if sync.update_product_content(product['id'], row, dry_run=dry_run):
            success += 1
        else:
            failed += 1
    
    # Save backup
    backup_path = f'backup_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json'
    sync.save_backup(backup_path)
    
    logger.info(f'\nSync complete: {success} updated, {failed} failed')
    logger.info(f'Backup saved to: {backup_path}')
    if dry_run:
        logger.info('This was a DRY RUN. Re-run with --execute to apply changes.')


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Sync AI content to Shopify')
    parser.add_argument('--input', required=True, help='Approved content CSV')
    parser.add_argument('--dry-run', action='store_true', default=True, help='Preview without changes')
    parser.add_argument('--execute', action='store_true', help='Apply changes to live store')
    parser.add_argument('--rollback', help='Rollback using backup JSON file')
    args = parser.parse_args()
    
    if args.rollback:
        sync = ShopifyContentSync(os.getenv('SHOPIFY_STORE_URL'), os.getenv('SHOPIFY_ACCESS_TOKEN'))
        sync.rollback(args.rollback)
    else:
        sync_approved_content(args.input, dry_run=not args.execute)

Content Quality Validation Agent

Type: agent An automated quality assurance agent that validates AI-generated product descriptions against the original product data to catch hallucinations, specification errors, and brand voice inconsistencies. Runs as a post-generation validation step before human review, flagging potential issues for the human reviewer's attention.

Implementation:

python
#!/usr/bin/env python3
"""
Content Quality Validation Agent
Validates AI-generated content against source product data.

Runs as a post-generation QA step, flagging issues before human review.

Usage: python qa_agent.py --products catalog.csv --content generated_content.csv --output qa_report.csv
"""

import csv
import json
import re
import logging
from dataclasses import dataclass, field

import openai

logger = logging.getLogger(__name__)

@dataclass
class QAResult:
    sku: str
    product_name: str
    issues: list = field(default_factory=list)
    severity: str = 'pass'  # pass, warning, fail
    score: float = 1.0


def check_word_count(description: str, min_words: int = 100, max_words: int = 300) -> list:
    """Check if description meets word count requirements."""
    word_count = len(description.split())
    issues = []
    if word_count < min_words:
        issues.append(f'Too short: {word_count} words (minimum {min_words})')
    if word_count > max_words:
        issues.append(f'Too long: {word_count} words (maximum {max_words})')
    return issues


def check_meta_lengths(meta_title: str, meta_description: str) -> list:
    """Check SEO meta field lengths."""
    issues = []
    if len(meta_title) > 60:
        issues.append(f'Meta title too long: {len(meta_title)} chars (max 60)')
    if len(meta_title) < 20:
        issues.append(f'Meta title too short: {len(meta_title)} chars (min 20)')
    if len(meta_description) > 155:
        issues.append(f'Meta description too long: {len(meta_description)} chars (max 155)')
    if len(meta_description) < 70:
        issues.append(f'Meta description too short: {len(meta_description)} chars (min 70)')
    return issues


def check_prohibited_patterns(text: str) -> list:
    """Check for prohibited content patterns."""
    issues = []
    # Check for fake urgency without campaign context
    urgency_phrases = ['limited time only', 'act now', 'don\'t miss out', 'while supplies last',
                       'hurry', 'only \\d+ left', 'selling fast']
    for phrase in urgency_phrases:
        if re.search(phrase, text.lower()):
            issues.append(f'Contains urgency language: "{phrase}" - verify this is appropriate')
    
    # Check for superlatives that may be unsubstantiated
    superlatives = ['best', 'finest', 'greatest', '#1', 'number one', 'world\'s', 'industry-leading']
    for word in superlatives:
        if word in text.lower():
            issues.append(f'Contains superlative "{word}" - verify this claim is substantiated')
    
    # Check for health/safety claims
    health_words = ['cure', 'treat', 'heal', 'prevent disease', 'clinically proven',
                    'doctor recommended', 'FDA approved', 'medical grade']
    for phrase in health_words:
        if phrase in text.lower():
            issues.append(f'CRITICAL: Contains health/safety claim "{phrase}" - requires verification')
    
    return issues


def check_specification_accuracy(description: str, product_data: dict) -> list:
    """Use AI to verify that description doesn't hallucinate specs not in source data."""
    client = openai.OpenAI()
    
    verification_prompt = f"""Compare this product description against the source product data.
Identify any claims, specifications, or features in the description that are NOT present in the source data.

Source Product Data:
{json.dumps(product_data, indent=2)}

Generated Description:
{description}

Return a JSON object with:
- "hallucinated_claims": Array of specific claims in the description not supported by source data
- "accuracy_score": Float 0-1 (1 = perfectly accurate, 0 = completely fabricated)
- "notes": Any additional observations
"""
    
    try:
        response = client.chat.completions.create(
            model='gpt-4.1-mini',
            messages=[{'role': 'user', 'content': verification_prompt}],
            response_format={'type': 'json_object'},
            temperature=0.1,
            max_tokens=500
        )
        result = json.loads(response.choices[0].message.content)
        
        issues = []
        for claim in result.get('hallucinated_claims', []):
            issues.append(f'Potential hallucination: {claim}')
        
        return issues, result.get('accuracy_score', 0.5)
    except Exception as e:
        logger.error(f'Specification check failed: {e}')
        return [f'Specification check error: {str(e)}'], 0.5


def run_qa_pipeline(products_file: str, content_file: str, output_file: str):
    """Run full QA pipeline on generated content."""
    # Load source product data
    with open(products_file, 'r', encoding='utf-8') as f:
        products = {row['sku']: row for row in csv.DictReader(f)}
    
    # Load generated content
    with open(content_file, 'r', encoding='utf-8') as f:
        content_rows = list(csv.DictReader(f))
    
    logger.info(f'Running QA on {len(content_rows)} generated descriptions')
    
    results = []
    for row in content_rows:
        sku = row.get('sku', '')
        qa = QAResult(sku=sku, product_name=row.get('product_name', ''))
        
        description = row.get('description_html', '')
        plain_text = re.sub('<[^<]+?>', '', description)  # Strip HTML
        
        # Run checks
        qa.issues.extend(check_word_count(plain_text))
        qa.issues.extend(check_meta_lengths(
            row.get('meta_title', ''),
            row.get('meta_description', '')
        ))
        qa.issues.extend(check_prohibited_patterns(plain_text))
        
        # Specification accuracy check (uses AI)
        if sku in products:
            spec_issues, accuracy = check_specification_accuracy(plain_text, products[sku])
            qa.issues.extend(spec_issues)
            qa.score = accuracy
        
        # Determine severity
        critical_issues = [i for i in qa.issues if 'CRITICAL' in i or 'hallucination' in i.lower()]
        if critical_issues:
            qa.severity = 'fail'
        elif qa.issues:
            qa.severity = 'warning'
        else:
            qa.severity = 'pass'
        
        results.append(qa)
    
    # Write QA report
    with open(output_file, 'w', newline='', encoding='utf-8') as f:
        writer = csv.writer(f)
        writer.writerow(['SKU', 'Product Name', 'Severity', 'Accuracy Score', 'Issues'])
        for r in results:
            writer.writerow([r.sku, r.product_name, r.severity, f'{r.score:.2f}', ' | '.join(r.issues)])
    
    # Summary
    pass_count = sum(1 for r in results if r.severity == 'pass')
    warn_count = sum(1 for r in results if r.severity == 'warning')
    fail_count = sum(1 for r in results if r.severity == 'fail')
    
    logger.info(f'\nQA Report Summary:')
    logger.info(f'  PASS: {pass_count}')
    logger.info(f'  WARNING: {warn_count}')
    logger.info(f'  FAIL: {fail_count}')
    logger.info(f'Report saved to: {output_file}')


if __name__ == '__main__':
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument('--products', required=True)
    parser.add_argument('--content', required=True)
    parser.add_argument('--output', default='qa_report.csv')
    args = parser.parse_args()
    run_qa_pipeline(args.products, args.content, args.output)

Testing & Validation

---

1
Run the duplicate content checker against all generated descriptions in the same category
2
Process a batch of 100 products through the bulk pipeline
3
Run shopify_sync.py with --dry-run flag against 10 approved products
4
Run shopify_sync.py with --execute flag for 3 test products
5
Run the QA agent against a test set with 3 accurate and 2 hallucinated descriptions
6
Execute a rollback using the backup JSON file after the live sync test
Duplicate Content Test
bash
# run against all generated descriptions in the same category

python check_duplicates.py
Bulk Generation Performance Test
bash
# process 100 products; record total time, API errors, cost, and success
# rate

python bulk_generate.py
Shopify Sync Dry Run
bash
# verify 10 approved products are matched by SKU and report what would be
# updated

python shopify_sync.py --dry-run
Shopify Sync Live
bash
# execute against 3 test products and verify descriptions, HTML rendering,
# and meta tags on the live store

python shopify_sync.py --execute
QA Agent Validation
bash
# run against test set of 3 accurate and 2 hallucinated descriptions

python qa_agent.py
Rollback Test
bash
# revert the 3 live test products to their original descriptions using the
# backup file

python shopify_sync.py --rollback backup_YYYYMMDD.json

Client Handoff

The client handoff session should be a 2-hour meeting with the client's marketing team and content approver, covering the following topics:

1. Training Topics (60 minutes)

  • How to use Shopify Magic / WooCommerce native AI for quick one-off descriptions (live demo)
  • How to use Hypotenuse AI for bulk generation: uploading products, selecting templates, reviewing output, exporting results (live demo with their actual products)
  • How to use Klaviyo AI for email campaigns: generating subject lines, body copy, A/B testing setup (live demo)
  • How to use the human review workflow: what to check, how to approve/reject, how to request edits
  • Common AI pitfalls to watch for: hallucinated specs, generic content, inappropriate urgency language
  • When to escalate to the MSP vs. handle in-house

2. Documentation to Leave Behind

  • Brand Voice Configuration Document: exact settings used in Hypotenuse AI, prompt templates, tone guidelines
  • Content Generation SOP: step-by-step procedures for each content type (product description, category page, email campaign)
  • Review Checklist: printed checklist for the content approver with all QA criteria
  • Login credentials document (stored in IT Glue/Hudu, shared securely)
  • API key inventory: which keys are active, what they're used for, when they expire
  • Emergency rollback procedure: how to restore content from backup if issues are discovered
  • Monthly content calendar template: suggested cadence for new content generation
  • Cost tracking guide: how to monitor API usage and SaaS subscription costs

3. Success Criteria Review

4. Transition to Managed Services

  • Define which tasks the client handles in-house (daily content generation, email campaigns) vs. what the MSP manages (platform configuration updates, bulk catalog refreshes, prompt optimization, tool upgrades)
  • Set up recurring monthly check-in schedule
  • Establish support ticket process for content quality issues or tool problems

Maintenance

1
Brand Voice Calibration (Monthly, 1 hour): Review 20 randomly sampled AI-generated descriptions from the past month. Score for brand voice consistency. If average score drops below 4.0, update brand voice settings in Hypotenuse AI and prompt templates. Client marketing team changes (new CMO, rebrand) trigger immediate recalibration.
2
Content Quality Audit (Monthly, 2 hours): Run the QA Agent (qa_agent.py) against all content generated in the past month. Review any 'fail' or 'warning' flagged items. Track hallucination rate over time — target <2% of generated content flagged. Investigate and correct any systematic issues (e.g., if the AI consistently gets a product attribute wrong).
3
SEO Performance Review (Monthly, 1 hour): Check Google Search Console for pages with AI-generated content. Monitor: impressions, clicks, average position for target keywords. Compare against pre-AI baseline. Flag any pages that have lost ranking since AI content was deployed.
4
Software Updates (Monthly, 30 minutes): Check for updates to Hypotenuse AI, WriteText.ai, Klaviyo, and any custom scripts. Apply updates during off-peak hours. Test that integrations still function after updates.
5
API Cost Monitoring (Monthly, 15 minutes): Review OpenAI API usage dashboard. Compare actual costs against budget. Alert if costs exceed 120% of projected monthly budget. Optimize model selection if costs are trending up (e.g., switch from GPT-4.1 to GPT-4.1-mini for appropriate use cases).
6
Model Update Evaluation (Quarterly, 2-4 hours): When OpenAI or Anthropic releases new models, evaluate them against current prompts using 50 test products. Compare output quality and cost. Recommend model upgrades when quality improves significantly or cost decreases. This is NOT retraining — it's evaluating new vendor model releases.
7
Prompt Template Optimization (Quarterly, 2 hours): Analyze client feedback on content quality. Identify patterns in edits the reviewer consistently makes. Update prompt templates to address recurring issues. A/B test updated prompts against originals on 25 products before deploying.
8
Compliance Check (Quarterly, 1 hour): Review FTC guidance updates. Check EU AI Act implementation timeline for any new requirements. Verify that content audit trail is being maintained. Ensure no AI-generated fake reviews have been created.

SLA Considerations

  • Response time for content quality issues: 4 business hours
  • Response time for tool outages: 2 business hours
  • Monthly content generation capacity guaranteed per service tier
  • 99.5% uptime for custom API pipelines (dependent on OpenAI/cloud provider SLAs)

Escalation Path

1
Tier 1 (Client): Content approver flags quality issue → submits ticket to MSP
2
Tier 2 (MSP L1): MSP tech reviews issue, adjusts prompt templates or tool settings
3
Tier 3 (MSP L2): Senior tech investigates systemic issues, API errors, integration failures
4
Tier 4 (Vendor): Escalate to Hypotenuse AI, OpenAI, or Shopify support for platform-level issues

Renewal/Reassessment Triggers

  • Client catalog grows by >50% (may need higher-tier subscription)
  • Client rebrands or significantly changes brand voice
  • Major ecommerce platform migration (e.g., WooCommerce to Shopify)
  • New AI model generation release that significantly changes capabilities/pricing (e.g., GPT-5)

Alternatives

Platform-Native AI Only (Shopify Magic + Klaviyo AI)

Use only the free AI features built into the client's existing Shopify and Klaviyo platforms, without procuring any additional AI content tools. Shopify Magic generates product descriptions directly in the admin panel, and Klaviyo AI generates email subject lines and body copy within the campaign editor. No additional software costs, no API integrations, no custom development.

Jasper AI as Primary Platform

Use Jasper AI instead of Hypotenuse AI as the primary content generation platform. Jasper is a broader marketing content platform with the strongest brand voice AI controls and the largest template library. It handles product descriptions, blog posts, social media, and email content in a single platform. Jasper also has a Solutions Partner Program for MSPs with co-marketing support and revenue share.

Custom OpenAI API Pipeline Only (No SaaS Platform)

Skip the SaaS content platform entirely and build a fully custom pipeline using OpenAI's API (GPT-4.1-mini for bulk descriptions, GPT-4.1 for category pages). All content generation is handled through the custom Python scripts provided in this guide. This approach offers maximum flexibility and lowest per-unit content cost.

Describely as Primary Platform

Use Describely instead of Hypotenuse AI for product catalog content management. Describely is specifically focused on product content at scale with strong catalog management features, enrichment workflows, and has been validated by enterprise retailers like Target Australia (98% accuracy rate).

Anthropic Claude API Instead of OpenAI

For the custom API pipeline approach, use Anthropic's Claude models instead of OpenAI GPT models. Claude Sonnet 4 for high-quality content ($3.00/$15.00 per 1M tokens input/output) and Claude Haiku for bulk generation ($1.00/$5.00 per 1M tokens).

Note

Recommend this when: the client prioritizes nuanced, long-form content quality over cost efficiency, or when the MSP already has Anthropic API infrastructure. Use the anthropic client library in place of the openai library — same API integration pattern overall.

Want early access to the full toolkit?