Dynamic Bundle Builder
An embedded Shopify app that mines a store's real order history for co-purchase patterns and automatically surfaces "frequently bought together" bundles on the storefront, with Claude writing the shopper-facing names and pitches under a hard spend cap.
// AI capabilities
- Claude (Anthropic API) turns scored bundle candidates into shopper-facing names and one-line pitches, batched and tuned to the target segment
- Hard global spend cap plus per-shop monthly budgets tied to subscription plan, with automatic fallback to a free heuristic namer when a budget is exhausted or the API key is missing
- Market-basket mining (FP-growth/apriori via mlxtend) computes association rules (support, confidence, lift) from real order history to drive which products get bundled
// Architecture flow
Overview
Dynamic Bundle Builder is an embedded Shopify app (Remix + Polaris + App Bridge) built for a real store (Mike Sport) and deployed at bundles.mikesport.tech. It reads a shop's order history through the Admin GraphQL API, finds which products are actually bought together, and turns those patterns into ready-made bundles that show up on product pages and in the cart. A merchant reviews and approves bundles in an embedded admin dashboard before they go live.
Problem
Merchants want the AOV lift that "frequently bought together" bundles provide, but manually curating bundles from gut feel does not scale and goes stale as inventory and buying patterns change. The app's own project rules add a second constraint: it may only read Shopify data (no write access to create discounts or products), and any AI usage is capped at a hard spend limit, so the whole system has to prove its value through attach rate and merchandising quality rather than automated discounting.
Approach
The core decision logic lives in a separate Python worker following a Perceive, Decide, Act, Learn pipeline. Perceive pulls order and catalog data. Decide mines market-basket association rules with mlxtend (FP-growth/apriori, filtered by minimum support and lift) at the category level rather than SKU level, because on a catalog of roughly 3,700 products SKU-level pairs are too sparse; it then fills each bundle with the best-selling in-stock product per category and layers in per-segment affinity. The top-scoring, guardrail-passing candidates are handed to Claude, which writes a shopper-facing name and pitch for each bundle, batched to stay within token limits. A cost-guard module enforces a hard global cap and per-shop monthly budgets keyed to the store's plan (Free/Pro/Unlimited); once a budget is exhausted the pipeline falls back to a deterministic heuristic namer so it never breaks. Approved bundles are surfaced on the storefront through a Theme App Extension (Liquid blocks for PDP, cart drawer, and bundle pills) that inherits the store's own theme tokens by default and is customizable in the theme editor.
Architecture
- Remix admin app (
app/) with routes for the dashboard, bundle creation/review, analytics, design customization, plans/billing, and an owner/super-admin screen for managing shops - Python worker (
worker/bundle_builder/) structured as perceive, decide, act, learn modules plus a pipeline orchestrator and nightly runner - Prisma/Postgres models for
ShopSettings,StoreDesign,Bundle(draft/review/live/paused/retired lifecycle with score breakdown for explainability), andBundlePerformance(impressions, attach rate, AOV with/without) - Theme App Extension (
extensions/bundle-display/) withbundle.liquid,bundle-pills.liquid, andcart-bundles.liquidblocks - Shopify Managed Billing wired into an app.plans route with Free/Pro/Unlimited tiers and coupon redemption
- Multi-tenant by design (everything keyed by shop) even though it currently runs as a custom app for one store
Tech stack
- Remix, React, TypeScript, Polaris, App Bridge (Shopify embedded admin)
- Prisma with PostgreSQL for session storage and app data
- Python 3.12 worker: pandas, mlxtend (association-rule mining), Anthropic SDK
- Shopify Admin GraphQL API (2025-04), Shopify Billing API, Theme App Extensions (Liquid)
- Docker/Caddy deployment to a VPS (bundles.mikesport.tech)
Engineering highlights
- Category-level (not SKU-level) affinity mining, chosen specifically because SKU pairs were too sparse on a real ~3,700-product catalog, with a documented fallback to SKU-level for smaller catalogs
- A dual-layer AI spend guard (global hard cap + per-shop monthly budget by plan) with an automatic, transparent fallback to heuristic bundle naming so a spent cap, missing key, or unfunded shop never breaks the pipeline
- Read-only Shopify scopes only (
read_inventory,read_orders,read_products), a deliberate constraint that shaped the whole product (no real discounts, attach-rate growth through UX instead) - Bundle scoring keeps a
scoreBreakfield per bundle so each signal's contribution to the ranking is explainable rather than a black box - A written Enhancement Plan documents concrete, evidence-based next steps (storefront performance, accessibility, admin polish, analytics, Shopify "Built for Shopify" review readiness) checked against the app's actual code
Outcome
Per the project's own Enhancement Plan, the app had already built bundles from roughly 8,000 real orders for the live store, with working bulk discount controls, a real analytics page, and the mandatory Shopify privacy/GDPR webhooks in place. Git history shows active iteration through late June 2026, including an approval workflow that gates what goes live on the storefront (Pending/Live/Rejected status tabs).
Lessons
- The original implementation plan scoped an ambitious autonomous agent with embeddings, pgvector memory, and per-customer intent decomposition; the shipped system is a leaner, evidence-grounded version that trades some of that ambition for guardrails (read-only access, a hard AI spend cap) that made it safe to run against a real store.
- Choosing category-level over SKU-level market-basket mining was a direct response to real-world data sparsity on the actual catalog, not a decision made in the abstract.
- Treating the AI budget as a hard-capped, monitored resource (with a free fallback) made it possible to ship LLM-generated copy in a cost-sensitive, multi-tenant context without risking runaway spend.
Want to dig deeper?
Ask my AI agent anything about how this was built, what tradeoffs I made, or how it could fit your team.
Ask my AI →// related projects
Shopify Product Label App
A private, single-store Shopify app that shows rule-driven marketing labels on product cards in collection and search pages, built first for the Crocs/mikesport store and then cloned and re-skinned as an isolated app for MikeSport Lebanon.
Homepage Curating Agent for Shopify
A planned Shopify app that rebuilds the storefront homepage and merchandising every morning around live demand and inventory, using a Perceive, Decide, Act, Learn loop with a staged autonomy ramp from shadow proposals to autonomous publishing.
SizeSport Virtual Fitting Room
A Shopify custom app that recommends apparel and footwear sizes for a multi-brand sports retailer, backed by an authoritative, provenance-tracked size-chart dataset scraped exclusively from official brand websites.