Scenario
Products sitting in warehouses need a sales push. You pull inventory levels from Shopify’s GraphQL API, identify high-stock / low-velocity items, and generate marketing copy for those products with urgency-appropriate messaging.Architecture
Code
import os, requests, time
STORE = os.environ["SHOPIFY_STORE"]
TOKEN = os.environ["SHOPIFY_ACCESS_TOKEN"]
MV = os.environ["MAVERA_API_KEY"]
SH = f"https://{STORE}.myshopify.com/admin/api/2024-10/graphql.json"
SH_H = {"X-Shopify-Access-Token": TOKEN, "Content-Type": "application/json"}
MB = "https://app.mavera.io/api/v1"
MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
QUERY = """{
productVariants(first: 100) {
edges { node {
title product { title productType priceRangeV2 { minVariantPrice { amount currencyCode } } }
inventoryItem { inventoryLevels(first: 5) { edges { node { available } } } }
}}
}
}"""
resp = requests.post(SH, json={"query": QUERY}, headers=SH_H)
resp.raise_for_status()
variants = []
for e in resp.json()["data"]["productVariants"]["edges"]:
n = e["node"]
stock = sum(l["node"]["available"] for l in n["inventoryItem"]["inventoryLevels"]["edges"])
if stock >= 50:
vel = max(1, stock // 48)
if vel <= 2:
variants.append({"product": n["product"]["title"], "variant": n["title"], "type": n["product"]["productType"],
"price": n["product"]["priceRangeV2"]["minVariantPrice"]["amount"],
"currency": n["product"]["priceRangeV2"]["minVariantPrice"]["currencyCode"],
"stock": stock, "velocity": vel, "weeks": stock // max(vel, 1)})
variants.sort(key=lambda x: -x["stock"])
print(f"{len(variants)} priority items")
for v in variants[:5]:
gen = requests.post(f"{MB}/generations", json={"prompt":
f"Product needing sales push:\n{v['product']} — {v['variant']}\nType: {v['type']}\nPrice: {v['price']} {v['currency']}\nStock: {v['stock']} units, ~{v['velocity']}/wk\n\nWrite: 1) Urgency description (2 sentences) 2) Social ad with CTA 3) Flash sale email subject 4) Banner headline"
}, headers=MH)
gen.raise_for_status()
print(f"\n--- {v['product']} ({v['variant']}) — {v['stock']} units ---\n{gen.json()['text']}")
time.sleep(0.3)
const STORE = process.env.SHOPIFY_STORE, TOKEN = process.env.SHOPIFY_ACCESS_TOKEN, MV = process.env.MAVERA_API_KEY;
const SH = `https://${STORE}.myshopify.com/admin/api/2024-10/graphql.json`;
const MB = "https://app.mavera.io/api/v1";
const MH = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };
const QUERY = `{ productVariants(first:100) { edges { node { title product { title productType priceRangeV2 { minVariantPrice { amount currencyCode } } } inventoryItem { inventoryLevels(first:5) { edges { node { available } } } } } } } }`;
(async () => {
const resp = await fetch(SH, { method: "POST", headers: { "X-Shopify-Access-Token": TOKEN, "Content-Type": "application/json" }, body: JSON.stringify({ query: QUERY }) });
const variants = (await resp.json()).data.productVariants.edges.map(e => {
const n = e.node, stock = n.inventoryItem.inventoryLevels.edges.reduce((s, l) => s + l.node.available, 0);
return { product: n.product.title, variant: n.title, type: n.product.productType, price: n.product.priceRangeV2.minVariantPrice.amount, currency: n.product.priceRangeV2.minVariantPrice.currencyCode, stock };
}).filter(v => v.stock >= 50).map(v => {
const vel = Math.max(1, Math.floor(v.stock / 48));
return { ...v, velocity: vel, weeks: Math.floor(v.stock / vel) };
}).filter(v => v.velocity <= 2).sort((a, b) => b.stock - a.stock);
console.log(`${variants.length} priority items`);
for (const v of variants.slice(0, 5)) {
const gen = await fetch(`${MB}/generations`, { method: "POST", headers: MH, body: JSON.stringify({
prompt: `Product needing push:\n${v.product} — ${v.variant}\nType: ${v.type}\nPrice: ${v.price} ${v.currency}\nStock: ${v.stock}, ~${v.velocity}/wk\n\nWrite: 1) Urgency description 2) Social ad 3) Flash sale subject 4) Banner headline`
}) });
console.log(`\n--- ${v.product} (${v.variant}) — ${v.stock} units ---\n${(await gen.json()).text}`);
await new Promise(r => setTimeout(r, 300));
}
})();
Example Output
12 priority items
--- Wool Blend Cardigan (Charcoal / L) — 340 units ---
1. The layering piece you'll reach for every cool morning — at a price that won't last. Italian merino-nylon for warmth without bulk.
2. Still searching for the perfect fall layer? Premium merino, relaxed fit, timeless charcoal. Shop now → [link]
3. 72 hours only: The cardigan everyone's eyeing is 25% off
4. Layer Up for Less — Wool Blend Cardigan, Limited-Time Price
Error Handling
Inventory levels require fulfillment scopes
Inventory levels require fulfillment scopes
inventoryLevels requires read_inventory scope. Update in Shopify Admin → Settings → Apps → your app → API scopes. Changes take effect immediately.Velocity estimation is approximate
Velocity estimation is approximate
The code estimates velocity from current stock divided by an assumed time window. For accurate velocity, query order line items per product over 90 days and divide by weeks.