Shopify — Channel Overview
Researched: May 2026
Why This Channel
Shopify powers ~4.5 million online stores globally. For Leadmetrics clients in retail, fashion, food, home goods, and lifestyle — Shopify is their primary revenue system. Connecting it turns Leadmetrics from a generic marketing tool into an e-commerce content engine.
The most immediate value: agents can write product descriptions, collection page copy, and promotional content using actual product data instead of generic prompts. The current workflow requires clients to manually paste product details into the agent chat; a Shopify connection eliminates that entirely.
The second value stream: sales data tells agents what to write about. If the “Summer Linen Collection” is converting at 12% but needs more traffic, the Content Brief Writer can create a targeted blog post and landing page for that specific collection.
Auth Mechanism
OAuth 2.0 (Shopify Partner App flow).
- Auth URL:
https://{shop}.myshopify.com/admin/oauth/authorize - Token URL:
https://{shop}.myshopify.com/admin/oauth/access_token - Scopes:
read_products read_orders read_customers read_analytics - Token type: permanent (Shopify offline access tokens do not expire; no refresh needed)
The shop subdomain ({shop}.myshopify.com) is required at connection time — stored as subChannelInfo.id = shop and channel.url = https://{shop}.myshopify.com.
The connection flow requires the user to enter their Shopify store URL first (the url field), then initiates the OAuth popup — same pattern as WordPress basic auth but with OAuth.
What It Enables
| Capability | Detail |
|---|---|
| Product data | Product titles, descriptions, images, prices, collections — available as RAG context for all agents |
| Sales analytics | Revenue, orders, top products, conversion rate, average order value |
| Customer segments | Total customers, new vs. returning, geographic distribution |
| Content generation | Blog post topics based on top-selling products; landing page copy per collection; product descriptions |
| Promotion planning | Activity planner aware of Shopify sale events and seasonal promotions |
| Insight worker | Top products by revenue; traffic-to-conversion rate; content-to-sales attribution |
Workers Needed
| Worker | Queue | Trigger |
|---|---|---|
shopify-sync.worker.ts (new) | agent__shopify-sync | On channel connect + nightly cron |
shopify-insights.worker.ts (new) | agent__shopify-insights | Weekly |
RAG Integration
Synced product and collection data is ingested into the RAG pipeline so all agents have context. The RAG dataset for Shopify:
| Dataset | Content |
|---|---|
shopify_products | Product title + description + price + collection + tags |
shopify_collections | Collection name + description + product count |
shopify_blog_posts | Existing Shopify blog posts (if any) — avoid duplication |
Ingested via the same rag-ingest queue pattern used for blog posts and web pages.
Insight Worker Output (ChannelInsight)
- Revenue and order count (last 30 days vs. prior 30 days)
- Top 5 products by revenue
- Conversion rate trend
- Average order value trend
- Most returned items (content opportunity: write better product descriptions)
- Suggested: blog topics and landing pages based on high-traffic, low-conversion products
Channel Detail Page — ShopifyChannelDetail.tsx
Tabs:
- Overview — store name, revenue, orders, conversion rate (last 30 days)
- Top Products — ranked table; click to generate content brief for that product
- Collections — list with product counts and revenue contribution
- Insights — AI-generated e-commerce performance summary
- Suggestions — content actions (write product description, create collection landing page, etc.)
Implementation Plan
Phase 1 — Connect + Product Sync
-
Create
packages/providers/shopify/src/index.ts:getAuthUrl(shop, callbackUrl, channelId)— Shopify OAuth URL with shop subdomainexchangeCode(shop, code)— returns permanentaccessTokengetProducts(shop, accessToken, pageInfo?)— paginatedGET /admin/api/2024-01/products.jsongetCollections(shop, accessToken)— custom + smart collectionsgetOrders(shop, accessToken, since?)— revenue + order datagetShopInfo(shop, accessToken)— store name, currency, timezoneverify(shop, accessToken)—GET /admin/api/2024-01/shop.json
-
Add connection flow to
apps/api/src/routers/channel-connect.ts:GET /shopify/connect→ requiresshopquery param (store URL); return auth URLGET /shopify/callback→ exchange code, store permanent token, close popup- Note: no page selection step — one token per store
-
Update channel add UI: Shopify requires the user to enter their store URL (
{store}.myshopify.com) in the Add Channel form before OAuth can start -
Create
shopify-sync.worker.ts:- Fetch products + collections → upsert to RAG + a lightweight
ShopifyProductcache table for the channel detail page - Fetch orders (last 90 days) → aggregate metrics for the detail page
- Fetch products + collections → upsert to RAG + a lightweight
-
Add seed entry to
packages/db/prisma/seed.ts -
Create
ShopifyChannelDetail.tsx
Phase 2 — Analytics + Insights
-
Pull Shopify Analytics data: sessions, conversion funnel, traffic sources
-
Create
shopify-insights.worker.ts
Phase 3 — Content Generation Actions
-
“Generate product description” button on product row → triggers
content-brief-writerwith product data as context -
“Create collection landing page” → triggers landing page writer with collection data pre-filled
DB Changes Needed
model ShopifyProduct {
id String @id @default(cuid())
tenantId String
channelId String
shopifyId String // Shopify product ID
title String
description String? @db.Text
price Decimal?
collections String[]
tags String[]
imageUrl String?
syncedAt DateTime @default(now())
tenant Tenant @relation(fields: [tenantId], references: [id])
channel ConnectedChannel @relation(fields: [channelId], references: [id])
@@unique([channelId, shopifyId])
@@index([tenantId])
}Seed Entry
// packages/db/prisma/seed.ts — CHANNEL_CATALOGUE
{
type: "Shopify",
name: "Shopify",
iconKey: "shopify",
description: "Connect your Shopify store to generate product content and track e-commerce performance.",
authenticationType: "oauth2",
requiresUrl: true, // user must enter store URL first
isActive: true,
categories: ["ecommerce"],
},