FS
← Back
● LIVE — Embedded Shopify App (Production + Staging)

Shopi Pulse

Shopify store optimization platform embedded directly in the Shopify admin: an AI agent reads live store data through the Admin API, reasons over multi-step tool calls, and executes changes (pricing, inventory, product content) behind tier-gated human-in-the-loop approvals. No login screen — the store itself is the identity.

NestJSNext.jsApp BridgePrismaPostgreSQLRedisTurborepoShopify APIClaude / OpenRouter
31AI Agent Tools
SSEStreaming Agent Loop
0Login Screens — Embedded Auth
3Billing Tiers (Shopify Native)

// Context

Shopify merchants sit on data they don't act on — pricing gaps, thin product descriptions, stale inventory. Shopi Pulse connects to a store via the Shopify Admin API and gives an AI agent a tool belt to inspect that data and propose concrete edits, instead of another static analytics dashboard.

// What I Built

▪
pnpm/Turborepo monorepo: NestJS backend API and Next.js (App Router) dashboard as separate apps, sharing packages for AI tooling, Shopify client, Prisma schema, and workflow/automation types.
▪
Streaming agent loop with tool use: Multi-turn agent service drives read tools (products, orders, inventory) and write tools (price/content updates) against the Shopify Admin API, streaming progress to the frontend over SSE with anti-buffering headers through the Cloudflare + reverse-proxy chain.
▪
Embedded-only auth, no login: The app lives inside the Shopify admin iframe (App Bridge v4). Install is cookieless: the frontend's signed session token is exchanged server-side for an expiring offline Admin API token — no OAuth redirect, no user table, no passwords. Shop identity comes only from HMAC-verified session tokens, never from query params.
▪
Tier-gated approval guardrails: Write actions land in a PendingAction queue for explicit approval, or auto-execute on paid tiers with a per-store toggle — tier limits enforced server-side on every request, independent of stored toggle state.
▪
Native Shopify billing: Subscription flow through Shopify's Billing API (appSubscriptionCreate) with FREE/PRO/CUSTOM tiers, deduplicated billing webhooks, synchronous downgrade on cancel, and per-store daily usage limits.
▪
Multi-provider LLM abstraction: Format-normalizing client layer supports Anthropic directly and OpenRouter as an Anthropic-compatible gateway, so model choice (Claude, GPT-4o, free-tier alternatives) is a config swap, not a rewrite.
▪
Persistent chat with full event history: Tool calls, tool results, and model identity are persisted per message (not just final text), so a reloaded chat shows exactly what the agent did, not a collapsed summary.

// Architecture

Shopify admin iframe→App Bridge session token→Next.js dashboard→SSE chat stream→NestJS agent service
→tool-use loop (read/write tools)→Shopify Admin API→PendingAction approval gate
Postgres + Prisma for persistence, Redis + BullMQ for store sync, native Shopify billing for subscriptions

// Key Engineering Decisions

▪
Model identity surfaced, not hidden: Free-tier models fail multi-step tool use in distinct, confusing ways — hallucinated IDs, silent truncation, dead-end turns with no action taken. Rather than paper over every failure shape server-side, the model that generated each response is persisted and shown, turning 'the app is broken' into 'this model isn't strong enough for this task.'
▪
Dead-end turns surfaced, not swallowed: The agent loop detects when a turn produces no tool call and no real answer (a stall) and raises it as an explicit error event instead of silently saving stale text as the final response.
▪
SSE through a proxy chain, done properly: Real-time streaming was silently buffered by the Cloudflare + reverse-proxy path. Fixed with explicit X-Accel-Buffering headers and forced header flush rather than switching transport.
▪
Reads are free, writes are tier-gated: Read tools execute immediately; write tools stage a PendingAction or auto-execute only when the store's billing tier allows it — enforcement lives in the per-request limit resolution, so a stale toggle left on after a downgrade can't bypass the gate.
▪
The store is the identity: No merchant accounts, sessions, or refresh tokens to manage. Every request's shop comes from a short-lived HMAC-signed session token; expiring offline Admin API tokens are refreshed through a single choke-point service every caller (sync, billing, AI tools) must go through.
▪
Live tests over mocks: An env-gated Jest suite runs all 31 agent tools against a real staging store. It caught two production GraphQL bugs that mocked unit tests masked — the mocks had encoded the broken schema. Write tools carry undo handlers so test mutations roll back.

// War Story

A production conversation looked frozen mid-task: the agent correctly diagnosed a conversion-optimization request, then went silent — no streaming, no tool calls, no error. Root cause turned out to be layered: SSE responses were buffered by the proxy chain (fixed with anti-buffering headers), and separately the tool-use loop could hit a turn with no tool call and no content, which the code was treating as a normal completion. Fixing both surfaced a further issue — a free-tier model truncating a response mid-table at 528 characters, invisible without knowing which model produced it. Model tracking closed that loop: every assistant message now carries the model that wrote it, so future failures are diagnosable from the chat itself.

app.pulse-ai.uk ↗
Shopi Pulse — Main Dashboard & Analytics

[01/04] — Store Analytics — Product performance, inventory pricing gaps, and action recommendations