Headless Commerce October 30, 2025 13 min read

AI Commerce: What Happens When Your Store Can Talk Back

Not a chatbot with canned responses. An AI that knows every product in your catalog, can search and compare them, and captures leads as a natural side effect of helping people.

Tyler Colby · Founder, Colby's Data Movers

Most "AI Chatbots" Are Just Fancy FAQ Pages

You have seen them. The little chat bubble in the corner. You click it. You type "Do you have this in red?" You get a response that says "Please check our product page for available colors, or contact support@store.com for assistance."

That is not AI. That is a keyword matcher with a language model wrapper. It does not know your catalog. It cannot search products. It cannot compare options. It cannot help someone make a buying decision. It is a FAQ page that requires typing instead of scrolling.

A real commerce AI is fundamentally different. It has the entire product catalog in its context. It can search, filter, and compare products. It can answer specific questions about specifications, compatibility, and availability. And it can capture email addresses and qualify leads as a natural part of the conversation, not as a popup or a form.

The difference between a FAQ chatbot and a commerce AI is the same as the difference between a store directory sign and a knowledgeable salesperson. One points you in a direction. The other understands what you need and helps you find it.

The Architecture

Here is what a real commerce AI looks like under the hood:

┌──────────────────────────────────────────┐
│          Chat UI (React Component)        │
│  - Message list                           │
│  - Text input                             │
│  - Product cards (rendered from tools)    │
│  - Email capture form (triggered by AI)   │
└────────────────┬─────────────────────────┘
                 │ POST /api/chat
                 ▼
┌──────────────────────────────────────────┐
│          Next.js API Route                │
│  - Vercel AI SDK (streaming)             │
│  - Claude model                    │
│  - System prompt (catalog + rules)        │
│  - Tool definitions                       │
└────────────────┬─────────────────────────┘
                 │ Tool calls
                 ▼
┌──────────────────────────────────────────┐
│          Tool Functions                   │
│  - searchProducts(query, filters)         │
│  - getProductDetails(handle)              │
│  - compareProducts(handles[])             │
│  - checkAvailability(variantId)           │
│  - captureEmail(email, context)           │
│  - createQuoteRequest(email, items)       │
└──────────────────────────────────────────┘

The key insight: the AI does not just generate text. It calls functions. When someone asks "What winch mounts do you have for a 2024 Tacoma?", the AI calls searchProducts with the right filters, gets structured product data back, and generates a response that includes actual product cards with images, prices, and buy links.

Tool Calling: The Part That Makes It Work

Tool calling (also called function calling) is the feature that separates a real commerce AI from a chatbot. Here is how it works in practice.

You define a set of functions the AI can call. Each function has a name, a description, and a schema for its parameters. The AI decides when to call which function based on the conversation.

// app/api/chat/route.js
import { streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { z } from 'zod';
import { getAllProducts, getProductByHandle } from '@/lib/shopify';

// Load catalog at startup for system prompt
let catalogCache = null;
async function getCatalog() {
  if (!catalogCache) {
    const products = await getAllProducts();
    catalogCache = products.map(p => ({
      handle: p.handle,
      title: p.title,
      type: p.product_type,
      vendor: p.vendor,
      tags: p.tags,
      price: p.variants[0]?.price,
      available: p.variants.some(v => v.available)
    }));
  }
  return catalogCache;
}

export async function POST(req) {
  const { messages } = await req.json();
  const catalog = await getCatalog();

  const result = streamText({
    model: anthropic('claude-sonnet-4-20250514'),
    system: buildSystemPrompt(catalog),
    messages,
    tools: {
      searchProducts: {
        description:
          'Search the product catalog by keyword, ' +
          'category, price range, or tag. Returns ' +
          'matching products with basic info.',
        parameters: z.object({
          query: z.string().describe(
            'Search terms'
          ),
          category: z.string().optional().describe(
            'Product type to filter by'
          ),
          maxPrice: z.number().optional().describe(
            'Maximum price filter'
          ),
          minPrice: z.number().optional().describe(
            'Minimum price filter'
          )
        }),
        execute: async ({ query, category,
                          maxPrice, minPrice }) => {
          let results = catalog.filter(p => {
            const text = `${p.title} ${p.type} ` +
              `${p.tags.join(' ')} ${p.vendor}`
                .toLowerCase();
            const matchesQuery =
              text.includes(query.toLowerCase());
            const matchesCategory = !category ||
              p.type?.toLowerCase() ===
                category.toLowerCase();
            const price = parseFloat(p.price);
            const matchesPrice =
              (!maxPrice || price <= maxPrice) &&
              (!minPrice || price >= minPrice);
            return matchesQuery &&
              matchesCategory && matchesPrice;
          });
          return results.slice(0, 6);
        }
      },

      getProductDetails: {
        description:
          'Get full details for a specific product ' +
          'including all variants, images, and ' +
          'description.',
        parameters: z.object({
          handle: z.string().describe(
            'Product handle (URL slug)'
          )
        }),
        execute: async ({ handle }) => {
          return await getProductByHandle(handle);
        }
      },

      compareProducts: {
        description:
          'Compare 2-4 products side by side. ' +
          'Returns details for each product.',
        parameters: z.object({
          handles: z.array(z.string()).describe(
            'Array of product handles to compare'
          )
        }),
        execute: async ({ handles }) => {
          const products = await Promise.all(
            handles.map(h => getProductByHandle(h))
          );
          return products.filter(Boolean);
        }
      },

      captureEmail: {
        description:
          'Capture a customer email address when ' +
          'they express interest in a product, ' +
          'want to be notified, or request info ' +
          'to be sent to them.',
        parameters: z.object({
          email: z.string().email(),
          context: z.string().describe(
            'Why the email was captured'
          ),
          productInterest: z.string().optional()
        }),
        execute: async ({ email, context,
                          productInterest }) => {
          // Send to HubSpot
          await fetch(
            'https://api.hubapi.com/crm/v3/' +
            'objects/contacts',
            {
              method: 'POST',
              headers: {
                'Authorization':
                  `Bearer ${process.env.HUBSPOT_TOKEN}`,
                'Content-Type': 'application/json'
              },
              body: JSON.stringify({
                properties: {
                  email,
                  lead_source: 'ai_chat',
                  product_interest:
                    productInterest || '',
                  ai_chat_context: context,
                  lifecyclestage: 'lead'
                }
              })
            }
          );
          return { captured: true };
        }
      }
    }
  });

  return result.toDataStreamResponse();
}

The System Prompt

The system prompt is where the AI gets its personality and its product knowledge. This is not a one-paragraph instruction. It is a detailed briefing that includes the full catalog summary and behavioral rules.

function buildSystemPrompt(catalog) {
  return `You are a product specialist for [Store Name].
You help customers find the right products for their
needs. You are knowledgeable, direct, and honest.

PERSONALITY:
- Be helpful but not pushy
- If you do not know something, say so
- Recommend products based on the customer's stated
  needs, not on what is most expensive
- Be honest about trade-offs between products
- Never make up specifications or features

PRODUCT CATALOG SUMMARY:
${JSON.stringify(catalog, null, 2)}

TOOLS:
- Use searchProducts when the customer describes what
  they need. Search before recommending.
- Use getProductDetails when discussing a specific
  product. Get the real data, do not guess.
- Use compareProducts when the customer is deciding
  between options.
- Use captureEmail ONLY when the customer:
  (a) asks you to send them information
  (b) asks about availability for an out-of-stock item
  (c) wants a quote for a custom order
  (d) explicitly provides their email
  Never ask for an email unprompted. Never pressure.

LEAD CAPTURE RULES:
- Email capture should feel natural, not forced
- Valid reasons: "I can email you the full specs",
  "I can notify you when it is back in stock",
  "I can have our team prepare a custom quote"
- Invalid reasons: "Before I help you, I need your
  email", "Sign up for our newsletter"
- If the customer says no, drop it immediately

RESPONSE FORMAT:
- Keep responses concise. 2-4 sentences plus
  product data.
- When showing products, include title, price, and
  a one-line description.
- When comparing, use a structured format.
- Always include a link to the product page.`;
}

Two things to notice. First, the full catalog summary is in the system prompt. For a store with 200 products, this is roughly 15-20KB of JSON. That fits comfortably in the context window. The AI can reference any product without a tool call. It only calls getProductDetails when it needs variant-level information or the full description.

Second, the lead capture rules are strict. The AI never asks for an email unless the conversation naturally creates a reason. This is important. Pushy email capture kills trust. Natural email capture converts at 20-35%.

What a Real Conversation Looks Like

Here is an actual conversation from a client store. Product details changed for privacy.

Customer: "I need a winch mount for my 2024 Tacoma.
What do you have?"

AI: [calls searchProducts("winch mount tacoma")]

AI: "We have three winch mount options that fit the
2024 Tacoma:

1. HD Winch Mount - Standard ($289)
   Fits 8,000-12,000 lb winches. Bolt-on install.

2. HD Winch Mount - Extended ($349)
   Same as Standard with 4" extension for larger
   bumpers.

3. Winch Plate Kit ($189)
   Universal plate. Requires drilling. Fits any
   winch up to 10,000 lb.

Which winch are you running? That will narrow it down."

Customer: "Warn VR EVO 12-S"

AI: [calls getProductDetails("hd-winch-mount-standard")]

AI: "The Warn VR EVO 12-S is a 12,000 lb winch with
a standard mounting pattern. The HD Winch Mount -
Standard ($289) is the right fit. It is rated for
12,000 lb, uses the standard 10x4.5 bolt pattern,
and bolts directly to the Tacoma frame without
drilling.

The Universal Plate Kit would also work but requires
drilling and is only rated to 10,000 lb. I would not
recommend it for a 12,000 lb winch.

Want me to pull up the full specs and install guide
for the HD Standard?"

Customer: "Yeah, can you send me the install guide?
I want to read it before I order."

AI: [calls captureEmail - but needs email first]

AI: "Sure. What email should I send the install guide
to?"

Customer: "mike@example.com"

AI: [calls captureEmail("mike@example.com",
  "Requested install guide for HD Winch Mount",
  "hd-winch-mount-standard")]

AI: "Done. I have sent the install guide to
mike@example.com. It includes the spec sheet and
a video walkthrough of the install process.

The HD Winch Mount - Standard is in stock and ships
next business day. Here is the direct link when you
are ready: [product link]"

Look at what happened. The AI searched the catalog. It recommended the right product based on the customer's specific winch. It explained why the cheaper option was not suitable. It offered to send documentation. The customer volunteered their email. The AI captured it in HubSpot with full context.

No popup. No form. No "Subscribe to our newsletter." Just a helpful conversation that ended with a qualified lead in the CRM.

The Chat UI

The frontend uses the Vercel AI SDK's useChat hook, which handles streaming, message history, and tool result rendering:

// components/ChatWidget.jsx
'use client';
import { useChat } from 'ai/react';
import { ProductCard } from './ProductCard';

export function ChatWidget() {
  const { messages, input, handleInputChange,
          handleSubmit, isLoading } = useChat({
    api: '/api/chat',
    initialMessages: [{
      role: 'assistant',
      content: 'Hi. I can help you find the right ' +
        'product. What are you looking for?'
    }]
  });

  return (
    <div className="chat-widget">
      <div className="chat-messages">
        {messages.map(msg => (
          <div
            key={msg.id}
            className={`message ${msg.role}`}
          >
            {msg.content}

            {/* Render product cards from tool calls */}
            {msg.toolInvocations?.map(tool => {
              if (tool.toolName === 'searchProducts'
                  && tool.result) {
                return tool.result.map(product => (
                  <ProductCard
                    key={product.handle}
                    product={product}
                  />
                ));
              }
              return null;
            })}
          </div>
        ))}

        {isLoading && (
          <div className="message assistant typing">
            Thinking...
          </div>
        )}
      </div>

      <form onSubmit={handleSubmit}>
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Ask about our products..."
          disabled={isLoading}
        />
        <button type="submit" disabled={isLoading}>
          Send
        </button>
      </form>
    </div>
  );
}

The key detail: tool invocations are part of the message stream. When the AI calls searchProducts, the results come back in the message and get rendered as ProductCard components. The buyer sees actual product cards with images and prices, not just text descriptions.

Cost and Performance

Let me give you real numbers because everybody asks.

AI MODEL COSTS (per conversation):
  Average messages per conversation: 6
  Average input tokens per message:  ~2,000
    (includes system prompt with catalog)
  Average output tokens per message: ~300
  Total tokens per conversation:     ~14,000

  Claude Sonnet cost per conversation:
    Input:  12,000 tokens x $3/M  = $0.036
    Output:  2,000 tokens x $15/M = $0.030
    Total: ~$0.07 per conversation

  Monthly cost at 2,000 conversations: ~$140

  Compare to: one customer service rep at $4,000/month
  handling maybe 400 conversations/month

Seven cents per conversation. At 2,000 conversations per month, that is $140. A human handling those same conversations costs 30x more and is unavailable at 2 AM on a Saturday.

The trade-off: the AI cannot handle every conversation. Complex custom orders, complaints, and technical issues that require domain expertise beyond the catalog need to escalate to a human. We build in an escalation path:

// In the system prompt:
ESCALATION RULES:
- If the customer expresses frustration, offer to
  connect them with the team directly
- If the question requires information not in the
  catalog (custom fabrication, bulk pricing, etc.),
  offer to have a specialist follow up
- If the customer asks the same question twice
  (indicating you did not help), apologize and offer
  a human connection
- Escalation message: "I want to make sure you get
  the right answer. Can I have one of our product
  specialists reach out to you? They can usually
  respond within a few hours."

Lead Quality

Not all leads are equal. An email captured from a popup converts at 2-4%. An email captured from an AI conversation converts at 8-15%. The difference is context.

A popup lead gave you their email to dismiss a popup. They may not even remember doing it. An AI lead gave you their email because they were deep in a product conversation. They asked a specific question about a specific product. They volunteered their email so you could send them information they requested.

The HubSpot contact for an AI lead includes: which products they asked about, what questions they had, what their use case is, and why they wanted to be contacted. The sales team has a complete brief before making the first call.

HubSpot contact properties from AI chat:

email:             mike@example.com
lead_source:       ai_chat
product_interest:  hd-winch-mount-standard
ai_chat_context:   "Requested install guide for HD
                    Winch Mount. Has 2024 Tacoma with
                    Warn VR EVO 12-S winch. Interested
                    in HD Standard ($289)."
lifecyclestage:    marketingqualifiedlead
hs_lead_status:    NEW

Compare that to a popup lead where all you know is: they have an email address.

What Does Not Work

Honest failures and limitations:

  • Image understanding is limited. If a customer sends a photo of their vehicle and asks "What fits this?", the AI cannot reliably identify the vehicle model from an image. It can ask clarifying questions instead, but the "send a photo" use case does not work yet.
  • Inventory accuracy lags. The catalog in the system prompt is refreshed every 5 minutes (ISR). If something sells out between refreshes, the AI might recommend an unavailable product. The checkAvailability tool call gets real-time data, but the AI does not always call it proactively.
  • Hallucination risk. Despite strict instructions, the AI occasionally invents a specification or feature that does not exist. The getProductDetails tool mitigates this by grounding responses in real data, but it is not perfect. Every AI response about specs should be verifiable against the product page.
  • Conversation length limits. Very long conversations (20+ messages) can exceed context windows or become expensive. We cap conversations at 20 messages and offer to continue via email.
  • Cold start latency. The first message takes 2-4 seconds to stream because the system prompt (with catalog) needs to be processed. Subsequent messages are faster (1-2 seconds).

Is It Worth Building?

Here are the numbers from one client store after 3 months with the AI assistant:

Monthly AI conversations:     1,800
Conversations with product
  recommendation:               1,200 (67%)
Email captures from AI:         380 (21% of conversations)
Orders attributed to AI
  conversations:                 94
Revenue attributed to AI:       $186,000
AI operating cost:               $126/month

ROI: roughly 1,500x

The $126/month includes Claude API costs and Vercel compute. It does not include the development cost to build it (roughly $8,000-15,000 for a well-integrated commerce AI). Even with development amortized over the first year, the ROI is absurd.

The caveat: these numbers are from a high-ticket store with a $2,000 AOV. If your AOV is $30, the economics are different. At $30 AOV, 94 orders is $2,820/month. Still positive, but the development cost takes longer to recoup.

For high-ticket stores, an AI commerce assistant is the highest-ROI feature you can add to your site. It costs almost nothing to run, it qualifies leads better than any form, and it works around the clock.

If you want to see this working on a live store, get in touch. We build these systems as part of our headless Shopify storefronts. The AI is not an add-on. It is integrated into the product page experience from the start.