Chapter 2: Product Requirements Document
2.1 Document Overview
Purpose
This Product Requirements Document (PRD) defines the complete functional, non-functional, integration, and user experience requirements for AdPriority. It serves as the authoritative reference for what the product must do, how it must perform, and what constraints it must operate within.
Every feature described in this document traces back to the core problem statement from Chapter 1: retailers waste ad spend because they cannot efficiently manage which products deserve the most advertising budget. Requirements that do not serve this purpose are out of scope.
Scope
This PRD covers AdPriority version 1.0 through version 2.0, spanning four development phases:
| Phase | Scope | Timeline |
|---|---|---|
| Phase 0 | Nexus Clothing MVP – manual feed pipeline, validated with real store | Week 1-2 |
| Phase 1 | SaaS Foundation – Shopify app with OAuth, database, basic priority UI, automated Sheets sync | Week 3-4 |
| Phase 2 | Full Product – seasonal automation, rules engine UI, new arrival boost, tag modifiers | Week 5-8 |
| Phase 3 | App Store Launch – billing integration, onboarding flow, App Store submission, scale to 100+ merchants | Week 9-12 |
Requirements marked with a phase tag indicate the earliest phase in which they must be implemented.
Audience
| Reader | How to Use This Document |
|---|---|
| Developer | Implementation reference for all features, data formats, and API contracts |
| Product Owner | Prioritization guide with requirement IDs for backlog management |
| QA Engineer | Acceptance criteria for test case design |
| Designer | UI/UX requirements and component specifications |
| Stakeholder | Feature scope validation and timeline alignment |
Version History
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | 2026-02-10 | Blueprint | Initial PRD compiled from research documents |
Requirement ID Conventions
All requirements use a prefix that indicates their category:
| Prefix | Category | Example |
|---|---|---|
FR- | Functional Requirement | FR-01: Priority Score Definition |
NFR- | Non-Functional Requirement | NFR-01: Page Load Time |
IR- | Integration Requirement | IR-01: Shopify App Installation |
UIR- | UI/UX Requirement | UIR-01: Dashboard Layout |
DR- | Data Requirement | DR-01: Nexus Catalog Baseline |
2.2 Product Description
What AdPriority Is
AdPriority is a Shopify embedded application that automates product priority scoring for Google Ads Performance Max (PMAX) campaigns. It assigns a 0-5 priority score to every product in a merchant’s catalog, then syncs those scores as custom labels to Google Merchant Center (GMC). Merchants use these labels to segment PMAX campaigns so that high-priority products receive more ad budget and low-priority products receive less or none.
Core Value Proposition
BEFORE AdPriority AFTER AdPriority
================ ================
All products treated equally Products scored 0-5 automatically
| |
v v
Budget spread evenly Budget allocated by priority
| |
v v
Shorts get ads in winter Shorts excluded in winter (score 0)
Dead stock gets impressions Dead stock excluded always (score 0)
New arrivals buried New arrivals boosted (score 5)
| |
v v
Poor ROAS 15-30% ROAS improvement
Hours of manual label management Automated, zero-maintenance
Product Boundaries
What AdPriority IS:
- A priority scoring system with a 0-5 scale
- A rules engine that calculates scores from product attributes, categories, seasons, and tags
- A sync pipeline that writes scores as custom labels to Google Merchant Center
- A seasonal automation engine that adjusts priorities on season boundaries
- A Shopify embedded app built with Polaris components
What AdPriority IS NOT:
- Not a bid management tool (it does not set bids or budgets in Google Ads)
- Not a feed management platform (it only writes custom labels, not full product feeds)
- Not an analytics dashboard (Pro tier adds read-only performance data, but it is not a reporting tool)
- Not a campaign builder (merchants still structure their PMAX campaigns manually)
- Not a multi-channel tool (v1.0 targets Google Ads only, not Facebook or Bing)
2.3 Functional Requirements
2.3.1 Priority Scoring System
FR-01: Priority Score Definition
Phase: 0 | Priority: P0
Every product receives a single integer score from 0 to 5 that directly controls how aggressively it is advertised in Google Ads.
| Score | Name | Budget Behavior | Typical Use Cases | GMC Label Value |
|---|---|---|---|---|
| 5 | Push Hard | Maximum budget, aggressive bidding | Seasonal bestsellers (shorts in summer, jackets in winter), hot new arrivals in first 14 days, proven high-margin items | priority-5 |
| 4 | Strong | High budget, balanced approach | Core performers with solid sales history, products with seasonal relevance, name brand items with strong recognition | priority-4 |
| 3 | Normal | Standard budget, conservative bidding | Year-round staples (jeans, fitted caps), average performers, baseline catalog items | priority-3 |
| 2 | Low | Minimal budget, strict ROAS targets | Off-season items still in stock, low-margin products, overstocked items, underwear and basics, accessories | priority-2 |
| 1 | Minimal | Very low budget, highest ROAS threshold only | End-of-season clearance, slow movers approaching dead stock, items with sale tags | priority-1 |
| 0 | Exclude | Zero ad spend | Archived products, dead stock (DEAD50 tagged), out-of-stock variants, gift cards, non-shoppable items (insurance, shipping protection) | priority-0 |
Constraints:
- Score must be an integer between 0 and 5 inclusive
- Every active product must have a score (no null scores)
- Default score for unclassified products is 2 (Low)
- Score is stored at the product level; variants inherit the product score unless individually overridden (future feature)
Acceptance Criteria:
-
Every product in the database has a
priority_scorecolumn with aCHECK (priority_score BETWEEN 0 AND 5)constraint - Products without matching rules receive default score of 2
-
Score values map to GMC label values using the format
priority-{score}
FR-02: Score Assignment Methods
Phase: 0 (manual), 1 (bulk, rules), 2 (seasonal, performance) | Priority: P0
AdPriority supports five methods of assigning priority scores, each appropriate for different use cases:
| Method | Description | Phase | User Action |
|---|---|---|---|
| Manual | Merchant sets score for an individual product via the UI | Phase 1 | Click product, select score from dropdown |
| Bulk | Merchant selects multiple products and assigns the same score | Phase 1 | Multi-select products, choose score, apply |
| Rules | Category-based automatic assignment (product type, collections, tags) | Phase 1 | Configure rules in rules engine, scores auto-calculate |
| Seasonal | Calendar-driven automatic adjustment based on season x category matrix | Phase 2 | Configure seasonal calendar, scores auto-adjust on season boundaries |
| Performance | ROAS-based recommendations suggesting score changes (Pro tier) | Phase 3 | Review recommendations, accept or dismiss |
Acceptance Criteria:
-
Manual scoring updates a single product and marks it as
priority_source = 'manual' - Bulk scoring updates up to 1,000 products in a single operation
- Rules-based scoring recalculates on product import and webhook events
- Seasonal scoring triggers automatically on season transition dates
- Performance recommendations are read-only suggestions, not automatic changes
FR-03: Score Override Hierarchy
Phase: 1 | Priority: P0
When multiple scoring methods apply to the same product, the highest-priority method wins. The hierarchy is evaluated top-to-bottom; the first matching rule determines the score.
SCORE OVERRIDE HIERARCHY (highest priority wins)
=================================================
1. Manual override -- Merchant locks a specific score
| (priority_locked = true)
v
2. Exclusion tags -- "archived" or "DEAD50" force score to 0
| (hard override, cannot be raised)
v
3. Inventory warnings -- "warning_inv_1" reduces score by 1
| (applied as modifier, not override)
v
4. New arrival boost -- Products created within 14 days get score 5
| (configurable duration and target score)
v
5. Tag modifiers -- "in-stock" adds +1, "NAME BRAND" adds +1,
| "Sale" subtracts -1
v
6. Seasonal calendar -- Category-specific seasonal adjustment
| (from the season x category matrix)
v
7. Category default -- Base score from product type group mapping
|
v
8. Global default -- Score 2 if no rules match
Modifier Stacking Rules:
- Tag modifiers stack additively (e.g.,
in-stock+1 andNAME BRAND+1 = +2 total) - Final score is clamped to the 0-5 range:
Math.min(5, Math.max(0, score)) - Override tags (
archived,DEAD50) short-circuit all other rules and return 0 immediately - Manual overrides are respected until the merchant unlocks the product
Real-World Example with Nexus Clothing Data:
Product: "Jordan Craig Stacked Jeans" (active, Winter season)
Step 1: Manual override? No (priority_locked = false)
Step 2: Exclusion tags? No
Step 3: Inventory warning? No
Step 4: New arrival? No (created > 14 days ago)
Step 5: Tag modifiers: NAME BRAND = +1 (store later)
Step 6: Seasonal calendar: Jeans & Pants, Winter = 4
Step 7: Category default: Jeans & Pants = 4 (not needed, seasonal applied)
Apply modifiers: 4 + 1 (NAME BRAND) = 5
Final priority: 5 (PUSH HARD)
Product: "Generic Mesh Shorts" (active, Winter season)
Step 1: Manual override? No
Step 2: Exclusion tags? No
Step 3: Inventory warning? No
Step 4: New arrival? No
Step 5: Tag modifiers: None relevant
Step 6: Seasonal calendar: Shorts, Winter = 0
Final priority: 0 (EXCLUDE)
Product: "Old Season Hoodie" (archived, DEAD50 tag)
Step 1: Manual override? No
Step 2: Exclusion tags? Yes -- "archived" found
Final priority: 0 (EXCLUDE) -- short-circuit, no further processing
Acceptance Criteria:
- Override hierarchy is enforced in the exact order specified
- Manual overrides persist until explicitly unlocked by merchant
- Exclusion tags short-circuit all subsequent rules
- Tag modifiers stack additively and result is clamped to 0-5
-
priority_sourcecolumn records which method determined the final score
2.3.2 Category Mapping
FR-10: Category Rules
Phase: 1 | Priority: P0
The category mapping system translates Shopify product types into priority scores. Because product type names can be long and hierarchical (Nexus uses a {Gender}-{Department}-{SubCategory}-{Detail} convention with 90 unique types), AdPriority groups them into manageable category groups.
Rule Capabilities:
| Capability | Description | Example |
|---|---|---|
| Product type match | Exact or partial match on product_type field | product_type = "Men-Tops-T-Shirts" |
| Product type contains | Substring match for hierarchical types | product_type CONTAINS "Shorts" |
| Collection membership | Product belongs to a specific collection | collection = "New Arrivals" |
| Tag-based rules | Product has a specific tag | tag = "clearance" |
| AND logic | Multiple conditions must all be true | product_type = "Shorts" AND season = "Summer" |
| OR logic | Any condition can be true | tag = "archived" OR tag = "DEAD50" |
| Temporal conditions | Time-based rules with expiry | collection = "New Arrivals" FOR 14 days |
Category Group Structure:
AdPriority organizes 90+ Nexus product types into 20 manageable groups. Each group maps multiple product types to a single set of priority rules.
| Group | Product Types Included | Product Count | Default Priority |
|---|---|---|---|
| T-Shirts | Men-Tops-T-Shirts, Boys-Tops-T-Shirts, Men-Tops-Crop-Top, Men-Tops-Tank Tops | 1,363 | 3 |
| Long Sleeve Tops | Men-Tops-T-Shirts-Long Sleeve | 54 | 3 |
| Jeans & Pants | Men-Bottoms-Pants-Jeans, Men-Bottoms-Stacked Jeans, Boys-Bottoms-Jeans, Men-Bottoms-Pants-Track Pants, Men-Bottoms-Pants-Cargo, Men-Bottoms-Stacked Track Pants | 911 | 4 |
| Sweatpants | Men-Bottoms-Pants-Sweatpants, Men-Bottoms-Stacked Sweatpants | 94 | 3 |
| Shorts | All *-Shorts-* types (8 types) | 315 | 3 |
| Swim Shorts | Men-Bottoms-Shorts-Swim-Shorts | 40 | 2 |
| Hoodies & Sweatshirts | Men-Tops-Hoodies & Sweatshirts, Boys-Tops-Hoodies | 264 | 3 |
| Outerwear - Heavy | Puffer Jackets, Shearling Coats | 72 | 3 |
| Outerwear - Medium | Denim Jackets, Varsity Jackets, Fleece, Sports, Vests | 232 | 3 |
| Outerwear - Light | Track Jackets, Windbreakers | 39 | 3 |
| Headwear - Caps | Fitted, Dad Hat, Snapback, Low Profile | 777 | 3 |
| Headwear - Cold Weather | Knit Beanies, Balaclavas | 171 | 2 |
| Headwear - Summer | Bucket Hats | 51 | 2 |
| Joggers | Men-Bottoms-Joggers, Boys-Bottoms-Joggers | 86 | 3 |
| Footwear - Sandals | Sandals & Slides | 58 | 2 |
| Footwear - Shoes | Men/Women Shoes | 48 | 3 |
| Underwear & Socks | Men/Women Underwear, Socks, Boys Underwear | 523 | 2 |
| Accessories | Bags, Jewelry, Belts, Wallet Chains | 350 | 2 |
| Women - Apparel | Women-Tops, Leggings, Dresses, Jumpsuits, Sets | 80 | 2 |
| Exclude | Bath & Body, Household Supplies, Gift Cards, Insurance, Shipping Protection | ~50 | 0 |
Acceptance Criteria:
- Merchant can create category groups that map multiple product types to a single priority
- Rules support AND/OR logic for combining conditions
- Unmapped product types receive the global default score (2)
- Category groups are stored per-store (multi-tenant safe)
- Changing a category rule triggers recalculation for all affected products
FR-11: Rule Examples with Nexus Data
Phase: 1 | Priority: P1
The rules engine must support the following patterns, all validated against real Nexus Clothing data:
# Seasonal category rules
IF product_type CONTAINS "Shorts" AND season = "Summer"
THEN priority = 5
IF product_type CONTAINS "Shorts" AND season = "Winter"
THEN priority = 0
IF product_type CONTAINS "Hoodies" AND season = "Winter"
THEN priority = 5
IF product_type CONTAINS "Puffer" AND season = "Summer"
THEN priority = 0
# Collection-based rules
IF collection = "New Arrivals"
THEN priority = 5 FOR 14 days
# Tag-based rules (overrides)
IF tag = "archived" OR tag = "DEAD50"
THEN priority = 0 (override)
IF tag = "clearance"
THEN priority = 1
# Tag-based rules (modifiers)
IF tag = "NAME BRAND"
THEN priority + 1
IF tag = "in-stock"
THEN priority + 1
IF tag = "Sale"
THEN priority - 1
IF tag = "warning_inv_1"
THEN priority - 1
# Vendor-based rules (future)
IF vendor = "Nexus Clothing"
THEN priority + 1 (store brand boost)
Acceptance Criteria:
- All rule patterns above can be expressed in the rules engine
- Rules can be tested with a “preview” mode that shows affected products without applying changes
- Rules are evaluated in priority order (see FR-03 hierarchy)
2.3.3 Product Mapping
FR-20: Product Identification
Phase: 0 | Priority: P0
AdPriority must correctly map Shopify product and variant identifiers to Google Merchant Center product IDs. The mapping is the foundation of the supplemental feed pipeline – an incorrect ID means the custom label will not be applied.
GMC Product ID Format (verified from live GMC export, 124,060 products):
Format: shopify_US_{productId}_{variantId}
Example: shopify_US_8779355160808_46050142748904
Components:
shopify -- Constant prefix (identifies source platform)
US -- Country code (ISO 3166-1 alpha-2)
8779355160808 -- Shopify product ID (13 digits)
46050142748904 -- Shopify variant ID (14 digits)
Item Group ID (parent product, used for variant grouping in GMC):
Format: {productId}
Example: 8779355160808
Verified Real Examples from Nexus GMC Export:
| Shopify Product ID | Shopify Variant ID | GMC Product ID |
|---|---|---|
| 8779355160808 | 46050142748904 | shopify_US_8779355160808_46050142748904 |
| 9128994570472 | 47260097118440 | shopify_US_9128994570472_47260097118440 |
| 9057367064808 | 47004004712680 | shopify_US_9057367064808_47004004712680 |
| 9238797418728 | 47750439567592 | shopify_US_9238797418728_47750439567592 |
| 7609551716584 | 42582395650280 | shopify_US_7609551716584_42582395650280 |
Storage Strategy: Hybrid approach (recommended)
| Layer | Role | Rationale |
|---|---|---|
| PostgreSQL database | Primary source of truth for priority scores and sync state | Full query capability, fast bulk operations, no API rate limits |
| Shopify metafields | Backup and recovery mechanism | Persists with product, survives database issues |
Metafield Schema:
{
"namespace": "adpriority",
"key": "config",
"type": "json",
"value": {
"priority": 4,
"priority_source": "seasonal",
"priority_locked": false,
"last_synced_at": "2026-02-10T10:00:00Z",
"custom_labels": {
"label_0": "priority-4",
"label_1": "winter",
"label_2": "jeans-pants",
"label_3": "in-stock",
"label_4": "name-brand"
}
}
}
Acceptance Criteria:
-
GMC product IDs are generated using the format
shopify_US_{productId}_{variantId} - All variant-level IDs are stored (GMC uses variant-level IDs exclusively)
-
Product mapping table has a computed
gmc_product_idcolumn - Mapping handles product ID changes gracefully (e.g., product delete + recreate)
FR-21: Custom Label Structure
Phase: 0 | Priority: P0
AdPriority uses all five Google Merchant Center custom label slots to provide rich segmentation data for Google Ads campaigns. The label schema was designed to stay well within GMC’s limits while providing maximum campaign segmentation flexibility.
| Label | Purpose | Example Values | Unique Values | GMC Limit |
|---|---|---|---|---|
custom_label_0 | Priority Score | priority-0 through priority-5 | 6 | 1,000 |
custom_label_1 | Season | winter, spring, summer, fall | 4 | 1,000 |
custom_label_2 | Category Group | t-shirts, jeans-pants, outerwear-heavy, headwear-caps | ~20 | 1,000 |
custom_label_3 | Product Status | new-arrival, in-stock, low-inventory, clearance, dead-stock | 5 | 1,000 |
custom_label_4 | Brand Tier | name-brand, store-brand, off-brand | 3 | 1,000 |
Total unique values across all labels: ~38 (well within the 5,000 total limit)
Status Determination Logic:
| Status | Condition | Priority Order |
|---|---|---|
dead-stock | Tag archived or DEAD50 present, or product status is archived | 1 (highest) |
low-inventory | Tag warning_inv_1 or warning_inv present | 2 |
new-arrival | Product created within last 30 days | 3 |
clearance | Tag Sale present | 4 |
in-stock | Default for active products with no special tags | 5 (lowest) |
Brand Tier Determination Logic:
| Tier | Condition |
|---|---|
store-brand | Vendor is “Nexus Clothing” |
name-brand | Tag NAME BRAND present, or vendor in recognized brand list (New Era, Jordan Craig, Psycho Bunny, LACOSTE, Gstar Raw, Ed Hardy, etc.) |
off-brand | Default for all other products |
Acceptance Criteria:
- All five custom labels are populated for every active variant in the supplemental feed
-
Label values use lowercase with hyphens (e.g.,
jeans-pants, notJeans & Pants) - Label values are within the 100-character maximum per value
- Total unique values per label do not exceed 1,000
2.3.4 Seasonal Calendar
FR-30: Season Definitions
Phase: 2 | Priority: P1
AdPriority uses a four-season calendar with configurable start/end dates. The default configuration follows the Northern Hemisphere retail calendar.
| Season | Default Start | Default End | Key Retail Events |
|---|---|---|---|
| Winter | December 1 | February 28/29 | Holidays, New Year, Valentine’s Day |
| Spring | March 1 | May 31 | Spring Break, Easter, Memorial Day |
| Summer | June 1 | August 31 | Vacation, July 4th, Back-to-school prep |
| Fall | September 1 | November 30 | Back to School, Thanksgiving, Black Friday, Cyber Monday |
ANNUAL SEASON TIMELINE
=======================
JAN | FEB | MAR | APR | MAY | JUN | JUL | AUG | SEP | OCT | NOV | DEC
|___WINTER____|______SPRING______|______SUMMER______|______FALL______|WIN
^ ^ ^ ^
Feb 28 May 31 Aug 31 Nov 30
Configuration Options:
| Setting | Default | Configurable | Notes |
|---|---|---|---|
| Hemisphere | Northern | Yes | Inverts season dates for Southern Hemisphere |
| Timezone | America/New_York | Yes | Used for season boundary calculations |
| Season dates | Fixed month boundaries | Yes (Growth+ tier) | Merchants can customize per season |
| Grace period | None | Yes (Growth+ tier) | 15-day grace period around transitions |
Acceptance Criteria:
- Season detection correctly identifies the current season based on date and timezone
- Season transitions trigger automatic priority recalculation
- Merchants can preview the impact of an upcoming season change before it takes effect
- Manual season override allows merchants to trigger early or late transitions
FR-31: Seasonal Rules
Phase: 2 | Priority: P1
Each category group has a configurable priority score per season. This forms the Category x Season matrix, which is the core of the seasonal automation engine.
Full Seasonal Priority Matrix (Nexus Clothing default configuration):
| Category Group | Winter | Spring | Summer | Fall | Default |
|---|---|---|---|---|---|
| T-Shirts | 2 | 4 | 5 | 3 | 3 |
| Long Sleeve Tops | 4 | 3 | 1 | 4 | 3 |
| Jeans & Pants | 4 | 4 | 3 | 5 | 4 |
| Sweatpants | 4 | 3 | 1 | 4 | 3 |
| Shorts | 0 | 3 | 5 | 1 | 3 |
| Swim Shorts | 0 | 2 | 5 | 0 | 2 |
| Hoodies & Sweatshirts | 5 | 3 | 1 | 5 | 3 |
| Outerwear - Heavy | 5 | 1 | 0 | 4 | 3 |
| Outerwear - Medium | 4 | 3 | 0 | 4 | 3 |
| Outerwear - Light | 2 | 4 | 1 | 3 | 3 |
| Headwear - Caps | 3 | 3 | 3 | 3 | 3 |
| Headwear - Cold Weather | 5 | 1 | 0 | 3 | 2 |
| Headwear - Summer | 0 | 3 | 4 | 2 | 2 |
| Joggers | 4 | 3 | 2 | 4 | 3 |
| Footwear - Sandals | 0 | 2 | 5 | 0 | 2 |
| Footwear - Shoes | 3 | 3 | 3 | 3 | 3 |
| Underwear & Socks | 2 | 2 | 2 | 2 | 2 |
| Accessories | 2 | 2 | 2 | 2 | 2 |
| Women - Apparel | 2 | 3 | 3 | 2 | 2 |
| Exclude | 0 | 0 | 0 | 0 | 0 |
Holiday Modifiers:
In addition to the seasonal matrix, specific shopping events can temporarily boost priorities:
| Event | Dates | Modifier | Affected Categories |
|---|---|---|---|
| Valentine’s Day | Feb 10-14 | +1 | Accessories, Jewelry |
| Easter | Variable | +1 | Spring items |
| Memorial Day | May (last Monday) | +1 | Summer items |
| July 4th | Jul 1-4 | +1 | Casual wear |
| Back to School | Aug 1 - Sep 15 | +2 | Jeans, T-Shirts, Hoodies |
| Labor Day | Sep (first Monday) | +1 | All categories |
| Black Friday | Nov (last Friday) | +2 | All categories |
| Cyber Monday | Monday after Black Friday | +2 | All categories |
| Christmas | Dec 1-25 | +2 | All categories |
Holiday Modifier Rules:
- Holiday modifiers are additive on top of the seasonal base score
- Final score is clamped to 0-5 after applying holiday modifiers
- Holiday modifiers do not override manual locks or exclusion tags
- Merchants can enable/disable individual holidays
- Custom holiday events can be created (Growth+ tier)
Acceptance Criteria:
- Seasonal priorities auto-apply on season transition dates
- Category x Season matrix is editable per merchant
- Holiday modifiers apply and expire on the configured dates
- Preview shows exact products and scores that will change on next transition
- Manual season trigger overrides the date-based automatic transition
2.3.5 New Arrival Automation
FR-40: New Arrival Boost
Phase: 2 | Priority: P1
New products added to the store automatically receive a high priority score for a configurable period, ensuring they get advertising visibility during their launch window.
Configuration Parameters:
| Parameter | Default | Range | Description |
|---|---|---|---|
enabled | true | Boolean | Enable or disable the new arrival boost |
days_threshold | 14 | 1-90 | Number of days a product is considered “new” |
boost_priority | 5 | 1-5 | Priority score during the boost period |
decay_enabled | false | Boolean | Gradually reduce priority over time |
decay_schedule | [5, 4, 3] | Array of scores | Scores at each decay interval |
decay_interval_days | 7 | 1-30 | Days between each decay step |
Behavior:
WITHOUT DECAY (default):
Day 0-14: Priority 5 (Push Hard)
Day 15+: Falls back to category/seasonal score
WITH DECAY ENABLED:
Day 0-7: Priority 5 (Push Hard)
Day 8-14: Priority 4 (Strong)
Day 15-21: Priority 3 (Normal)
Day 22+: Falls back to category/seasonal score
Interaction with Override Hierarchy:
- New arrival boost sits at position 4 in the hierarchy (see FR-03)
- Manual overrides take precedence over new arrival boost
- Exclusion tags (
archived,DEAD50) take precedence over new arrival boost - Inventory warnings take precedence over new arrival boost
- New arrival boost takes precedence over tag modifiers, seasonal rules, and category defaults
Detection Method:
- Shopify
created_attimestamp compared against current date - Products detected via
products/createwebhook for real-time processing - Daily reconciliation job catches any missed webhook events
Acceptance Criteria:
-
Products created within
days_thresholddays automatically receiveboost_priorityscore - Boost expires silently after the threshold period, reverting to standard scoring
- Decay schedule (when enabled) reduces score on the configured interval
- Configuration is per-store, not global
- Manual overrides are not affected by new arrival boost
2.3.6 Google Merchant Center Sync
FR-50: Sync Methods
Phase: 0 (Sheets), 2 (Content API), 1 (CSV) | Priority: P0
AdPriority supports three methods of syncing custom labels to Google Merchant Center, available at different subscription tiers:
| Method | Tier | Latency | Merchant Setup | Technical Complexity |
|---|---|---|---|---|
| Google Sheets supplemental feed | All tiers | Daily (GMC fetches automatically) | One-time: share Sheet URL, add as supplemental feed in GMC | Low – uses Sheets API only |
| Content API for Shopping | Pro, Enterprise | Near-real-time (within 2x/day limit) | One-time: connect Google account via OAuth | Medium – requires GMC OAuth |
| Manual CSV export | All tiers (fallback) | Manual upload by merchant | Download CSV, upload to GMC | None – offline process |
Google Sheets Pipeline (MVP and primary method):
AdPriority Database
|
| Google Sheets API (googleapis/sheets/v4)
v
Google Sheet (shared publicly, Viewer access)
+----------------------------------------------------------------+
| id | custom_label_0 | ... |
| shopify_US_8779355160808_46050142748904 | priority-5 | ... |
| shopify_US_9128994570472_47260097118440 | priority-4 | ... |
+----------------------------------------------------------------+
|
| GMC auto-fetches daily (or on manual trigger)
v
Google Merchant Center
|
| Labels applied to matching products
v
Google Ads PMAX Campaigns
Feed Size Estimates for Nexus:
| Metric | Value |
|---|---|
| Active products in Shopify | 2,425 |
| Estimated active variants in GMC | ~15,000-20,000 |
| Rows in supplemental feed | ~15,000-20,000 |
| Columns per row | 6 (id + 5 custom labels) |
| Total cells | ~120,000 |
| Google Sheets cell limit | 10,000,000 |
| Usage percentage | ~1.2% |
Acceptance Criteria:
-
Google Sheets supplemental feed is generated with correct column names (
id,custom_label_0throughcustom_label_4) - Product IDs in the Sheet exactly match GMC primary feed IDs (case-sensitive)
- Sheet is shared with “Anyone with the link” at Viewer access
- CSV export produces a downloadable file with the same structure
-
Content API integration (Pro tier) uses
products.custombatchfor efficiency
FR-51: Sync Schedule
Phase: 1 | Priority: P0
| Trigger | Behavior | Latency |
|---|---|---|
| Priority change | Mark product as needs_sync = true in database | Immediate (database) |
| Scheduled bulk sync | Write all pending changes to Google Sheet | Configurable: hourly or daily |
| Manual trigger | Merchant clicks “Sync Now” button | Immediate write to Sheet |
| Season transition | Bulk recalculate and sync all affected products | Within 1 hour of transition |
| Product webhook | Recalculate single product, queue for next sync | Next scheduled sync |
Sync Status Dashboard:
The UI must display:
- Last sync timestamp
- Number of products pending sync
- Number of products successfully synced
- Number of sync errors
- “Sync Now” button for manual trigger
Acceptance Criteria:
- Scheduled sync runs at merchant-configured intervals (minimum: daily)
- Manual sync trigger writes to Sheet within 60 seconds
- Sync status is visible on the dashboard
-
Products with
needs_sync = trueare included in the next scheduled sync -
After successful sync,
needs_syncis set tofalseandlast_synced_atis updated
FR-52: Error Handling
Phase: 1 | Priority: P1
| Error Scenario | Detection | Resolution | Merchant Impact |
|---|---|---|---|
| Google Sheets API quota exceeded | API returns 429 | Exponential backoff retry (3 attempts, 1s/2s/4s delay) | Sync delayed, not lost |
| Sheet permission revoked | API returns 403 | Alert merchant, provide re-authorization link | Sync paused until resolved |
| GMC feed processing error | GMC dashboard shows errors | Log error, alert merchant, provide troubleshooting link | Labels not applied for affected products |
| Product ID mismatch | Reconciliation job detects unmatched products | Log warning, exclude from feed | Affected products have no labels |
| Network timeout | Request times out after 30 seconds | Retry with backoff | Sync delayed, not lost |
Retry Configuration:
Max retries: 3
Initial delay: 1,000 ms
Backoff multiplier: 2
Maximum delay: 30,000 ms
Acceptance Criteria:
- All sync errors are logged with timestamp, error type, and affected product IDs
- Retry logic uses exponential backoff for transient failures
- Persistent failures (3+ consecutive) trigger a merchant notification
- Manual CSV export is always available as a fallback when automated sync fails
- Error count is displayed on the sync status dashboard
2.3.7 Google Ads Integration (Pro Tier)
FR-60: Performance Data
Phase: 3 | Priority: P2
Pro tier merchants can connect their Google Ads account to view performance metrics segmented by priority tier.
| Data Point | Source | Update Frequency |
|---|---|---|
| Impressions per priority tier | Shopping Performance View | Daily |
| Clicks per priority tier | Shopping Performance View | Daily |
| Cost per priority tier | Shopping Performance View | Daily |
| Conversions per priority tier | Shopping Performance View | Daily |
| ROAS per priority tier | Calculated (conversion value / cost) | Daily |
| Spend by category group | Shopping Performance View + category mapping | Daily |
| Trend data (30/60/90 day) | Historical aggregation | Daily |
GAQL Query Example:
SELECT
segments.product_custom_attribute0, -- priority score label
metrics.impressions,
metrics.clicks,
metrics.cost_micros,
metrics.conversions,
metrics.conversions_value
FROM shopping_performance_view
WHERE segments.date DURING LAST_30_DAYS
Acceptance Criteria:
- Performance data is fetched daily and stored in the database
- Dashboard displays ROAS, spend, and conversion data grouped by priority tier
- Historical trend charts show 30, 60, and 90-day windows
- Data is read-only (AdPriority does not modify Google Ads settings)
FR-61: Recommendations
Phase: 3 | Priority: P3
Based on performance data, AdPriority generates actionable recommendations for priority score adjustments.
| Recommendation Type | Trigger | Suggested Action |
|---|---|---|
| Increase priority | Product has ROAS > 2x average for its tier | “Move from priority 3 to priority 4” |
| Decrease priority | Product has ROAS < 0.5x average for its tier | “Move from priority 4 to priority 2” |
| Budget waste alert | High spend, zero conversions for 14+ days | “Consider excluding (priority 0)” |
| Seasonal prediction | Historical data shows seasonal demand pattern | “Shorts trending up – consider early Summer transition” |
Acceptance Criteria:
- Recommendations are displayed as dismissible cards in the dashboard
- Each recommendation shows the current score, suggested score, and supporting data
- Merchant can accept (applies change) or dismiss (hides recommendation)
- Dismissed recommendations do not reappear for the same product within 30 days
2.4 Non-Functional Requirements
2.4.1 Performance Requirements
| ID | Requirement | Target | Measurement Method |
|---|---|---|---|
| NFR-01 | Page load time | < 2 seconds | Lighthouse performance score |
| NFR-02 | Sync latency (Sheet write) | < 5 minutes for full catalog | Timed Sheets API operation |
| NFR-03 | Bulk operations | 1,000 products per minute | Timed batch update |
| NFR-04 | API response time | < 500ms (95th percentile) | Server-side APM logging |
| NFR-05 | Score calculation | 5,000 products in < 30 seconds | Timed batch calculation |
| NFR-06 | Product import speed | 1,000 products per minute | Timed Shopify API pagination |
| NFR-07 | Sheet generation | 20,000 rows in < 2 minutes | Timed Sheets API write |
| NFR-08 | Webhook processing | < 5 seconds per event | Event timestamp to completion |
2.4.2 Scalability Requirements
| Tier | Max Products | Max Variants (est.) | Max Syncs/Day | Max Users | Max Rules |
|---|---|---|---|---|---|
| Starter ($29/mo) | 500 | ~2,000 | 24 | 2 | 10 |
| Growth ($79/mo) | 10,000 | ~40,000 | 96 | 5 | 50 |
| Pro ($199/mo) | 100,000 | ~400,000 | 288 | 10 | Unlimited |
| Enterprise (Custom) | Unlimited | Unlimited | Unlimited | Unlimited | Unlimited |
Infrastructure Scaling Approach:
| Load Level | Strategy |
|---|---|
| < 50 merchants | Single PostgreSQL database, single app server, Google Sheets sync |
| 50-500 merchants | Connection pooling, Redis caching, background job queue (Bull) |
| 500+ merchants | Read replicas, horizontal app scaling, Content API for high-volume stores |
2.4.3 Security Requirements
| ID | Requirement | Implementation | Phase |
|---|---|---|---|
| NFR-10 | OAuth 2.0 authentication | Shopify token exchange flow + Google OAuth | Phase 1 |
| NFR-11 | Session token validation | JWT verification with client secret, audience, expiry | Phase 1 |
| NFR-12 | Webhook HMAC verification | SHA-256 HMAC validation on all webhook payloads | Phase 1 |
| NFR-13 | Encryption at rest | AES-256 for stored OAuth tokens | Phase 1 |
| NFR-14 | Encryption in transit | HTTPS/TLS everywhere (no mixed content) | Phase 1 |
| NFR-15 | GDPR compliance | Three mandatory webhooks, no customer PII stored | Phase 1 |
| NFR-16 | CCPA compliance | Data export and deletion on request | Phase 1 |
| NFR-17 | Shop-scoped data isolation | Every database query includes store_id filter | Phase 1 |
| NFR-18 | No customer PII stored | AdPriority stores only product data, never customer data | Phase 0 |
| NFR-19 | Minimal scope requests | Only read_products, write_products, read_inventory | Phase 1 |
2.4.4 Availability Requirements
| ID | Requirement | Target | Phase |
|---|---|---|---|
| NFR-20 | Uptime SLA | 99.9% (< 8.77 hours downtime/year) | Phase 3 |
| NFR-21 | Graceful degradation | Manual CSV export always available if automated sync fails | Phase 1 |
| NFR-22 | Automated backups | Daily database backups with 30-day retention | Phase 1 |
| NFR-23 | Disaster recovery | Restore from backup within 4 hours | Phase 2 |
| NFR-24 | Zero data loss | All priority scores and rules recoverable after any failure | Phase 1 |
2.5 Integration Requirements
2.5.1 Shopify Integration
IR-01: App Installation
Phase: 1 | Priority: P0
| Requirement | Specification |
|---|---|
| Authentication flow | OAuth 2.0 with token exchange (modern flow, no cookies) |
| App type | Embedded (mandatory for new apps as of late 2024) |
| App Bridge version | v4+ (latest) |
| UI framework | Shopify Polaris v13.9+ |
| Session management | Session tokens (JWTs signed with client secret) |
Session Token Claims:
| Claim | Description | Example |
|---|---|---|
iss | Shop admin URL | https://nexus-clothes.myshopify.com/admin |
dest | Shop URL | https://nexus-clothes.myshopify.com |
aud | Client ID | AdPriority app client ID |
sub | User ID | Shopify staff member ID |
exp | Expiration | Unix timestamp (1-minute validity) |
jti | Token ID | Unique identifier |
sid | Session ID | Session identifier |
IR-02: Data Access
Phase: 1 | Priority: P0
Required API Scopes:
| Scope | Purpose | Justification (for App Store review) |
|---|---|---|
read_products | Fetch product data (titles, types, tags, collections, variants) | “AdPriority reads product information to apply priority scoring rules and generate Google Merchant Center custom labels.” |
write_products | Store priority scores in product metafields | “AdPriority stores priority scores and sync status in product metafields for persistent data across sessions.” |
read_inventory | Check stock levels for inventory-based rules | “AdPriority uses inventory levels to adjust priority scores (lower priority for out-of-stock items).” |
Data Retrieved from Shopify:
| Data Type | API Endpoint | Fields Used |
|---|---|---|
| Products | GET /admin/api/2024-01/products.json | id, title, product_type, vendor, tags, status, created_at, handle |
| Variants | Included in products response | id, sku, inventory_quantity, price |
| Collections | GET /admin/api/2024-01/collections.json | id, title, products |
| Metafields | GET /admin/api/2024-01/products/{id}/metafields.json | namespace=adpriority |
IR-03: Webhooks
Phase: 1 | Priority: P0
Mandatory GDPR Webhooks (required for App Store approval – app will be rejected without these):
| Webhook | Endpoint | Behavior |
|---|---|---|
customers/data_request | POST /api/webhooks/customers-data-request | Return 200 OK with note “No customer PII stored” |
customers/redact | POST /api/webhooks/customers-redact | Return 200 OK (no action needed, no customer data stored) |
shop/redact | POST /api/webhooks/shop-redact | Delete all store data (rules, products, sync logs, settings) |
App Lifecycle Webhooks:
| Webhook | Endpoint | Behavior | Priority |
|---|---|---|---|
app/uninstalled | POST /api/webhooks/app-uninstalled | Delete all store data, cancel billing subscription | P0 |
Product Webhooks:
| Webhook | Endpoint | Behavior | Priority |
|---|---|---|---|
products/create | POST /api/webhooks/products-create | Apply rules to new product, calculate priority, queue for sync | P1 |
products/update | POST /api/webhooks/products-update | Check for type/tag changes, recalculate priority if changed | P2 |
products/delete | POST /api/webhooks/products-delete | Remove from product mapping, remove from supplemental feed | P2 |
Webhook Security:
All webhooks must verify the X-Shopify-Hmac-Sha256 header using HMAC-SHA256 with the app’s client secret. Unverified webhooks must be rejected with 401 Unauthorized.
2.5.2 Google Merchant Center
IR-10: Authentication
Phase: 0 (Sheets only, no auth), 2 (Content API) | Priority: P0 (Sheets), P2 (API)
| Method | Tier | OAuth Scope | Setup |
|---|---|---|---|
| Google Sheets API | All | https://www.googleapis.com/auth/spreadsheets | App creates and manages Sheet programmatically |
| Content API for Shopping | Pro, Enterprise | https://www.googleapis.com/auth/content | Merchant connects Google account via OAuth |
| Service accounts | Enterprise | N/A (service account key) | Generated in Google Cloud Console |
IR-11: API Operations
Phase: 2 (Content API) | Priority: P2
| Operation | API Endpoint | Purpose |
|---|---|---|
| List products | GET /content/v2.1/{merchantId}/products | Verify product existence in GMC |
| Update labels | PATCH /content/v2.1/{merchantId}/products/{productId} | Set custom labels via Content API |
| Batch update | POST /content/v2.1/products/batch | Update multiple products in one request |
| Product status | GET /content/v2.1/{merchantId}/productstatuses | Check for feed processing errors |
Rate Limits:
| Limit | Value | Impact |
|---|---|---|
| Product updates per day | 2x per product | Daily sync schedule is within limits |
| Requests per minute | Dynamic (throttling-based) | Implement exponential backoff |
| Batch size | Up to 10,000 entries per batch request | Sufficient for most stores |
2.5.3 Google Ads API (Pro Tier)
IR-20: Authentication
Phase: 3 | Priority: P2
| Requirement | Specification |
|---|---|
| OAuth scope | https://www.googleapis.com/auth/adwords |
| Developer token | Required (apply through Google Ads API Center) |
| Manager accounts | Supported for agency users (Enterprise tier) |
| Customer ID | Merchant provides their Google Ads customer ID |
IR-21: API Operations
Phase: 3 | Priority: P2
| Operation | Purpose | Frequency |
|---|---|---|
| Shopping Performance View | Product-level metrics (impressions, clicks, cost, conversions) | Daily fetch |
| Campaign data | Campaign-level performance | Daily fetch |
| Asset group metrics | PMAX asset group performance | Daily fetch |
| Listing group filters | Verify custom label-based product grouping | On demand |
2.6 UI/UX Requirements
UIR-01: Dashboard
Phase: 1 | Priority: P0
The dashboard is the landing page of the app, providing an at-a-glance view of the store’s priority scoring status.
| Component | Content | Polaris Component |
|---|---|---|
| Priority distribution chart | Bar or donut chart showing product count per score (0-5) | Custom chart (Chart.js) in Card |
| Sync status card | Last sync time, pending count, error count, “Sync Now” button | Card with Badge indicators |
| Upcoming seasonal changes | Next season transition date, affected product count, preview link | Card with Banner (informational) |
| Quick stats | Total products scored, active rules, last sync, current season | Layout with Card grid |
| Recent activity | Timeline of recent priority changes, syncs, rule changes | Card with list |
UIR-02: Product Management
Phase: 1 | Priority: P0
| Component | Behavior | Polaris Component |
|---|---|---|
| Product list | Paginated table (50-100 per page) showing product title, type, current priority, sync status | IndexTable or DataTable |
| Search | Filter by product title, SKU, or vendor | TextField with search icon |
| Filters | Filter by priority score, category group, season, sync status, product status | Filters with ChoiceList |
| Inline priority edit | Click on priority badge to change score via dropdown | Select in IndexTable cell |
| Bulk select | Checkbox selection for multi-product actions | IndexTable built-in selection |
| Bulk edit | Apply priority score to all selected products | BulkActions toolbar |
| CSV export | Download current filtered view as CSV | Button action |
| Priority badge | Color-coded badge showing score (red=0, orange=1-2, yellow=3, green=4-5) | Badge with tone variants |
UIR-03: Rules Engine
Phase: 2 | Priority: P1
| Component | Behavior | Polaris Component |
|---|---|---|
| Rule list | Ordered list of all active rules with name, type, affected count | IndexTable |
| Visual rule builder | IF-THEN interface for creating rules without code | Card with Select, TextField |
| Rule testing preview | Shows which products would be affected before applying | Modal with DataTable |
| Rule priority ordering | Drag-and-drop to reorder rules | Custom drag handle or up/down buttons |
| Enable/disable toggle | Toggle individual rules on/off without deleting | Toggle switch |
| Rule templates | Pre-built rules for common patterns (seasonal, new arrival, clearance) | Card with Button actions |
UIR-04: Seasonal Calendar
Phase: 2 | Priority: P1
| Component | Behavior | Polaris Component |
|---|---|---|
| Visual calendar | Timeline view showing season boundaries across 12 months | Custom component |
| Season configuration | Edit start/end dates per season | DatePicker or TextField |
| Category x Season matrix | Editable grid showing priority per category per season | DataTable with inline Select |
| Preview future changes | Show products affected by next transition | Button to Modal with preview |
| Current season indicator | Highlight current season with countdown to next transition | Banner (informational) |
| Manual trigger | Button to manually switch to a different season | Button (destructive styling) |
UIR-05: Settings
Phase: 1 | Priority: P0
| Component | Behavior | Polaris Component |
|---|---|---|
| Google account connection | OAuth flow to connect Google account (Sheets, GMC, Ads) | Card with Button |
| Sync preferences | Configure sync frequency (hourly, daily, manual) | RadioButton group |
| Notification settings | Toggle email alerts for sync errors, seasonal transitions | Checkbox list |
| New arrival configuration | Set boost duration, priority, decay options | TextField with Checkbox |
| Billing management | View current plan, upgrade/downgrade | Card with plan comparison |
| Data export | Export all priority data as CSV | Button action |
| Delete account | Remove all store data from AdPriority | Button (destructive) with Modal confirmation |
2.7 Data Requirements
DR-01: Nexus Catalog Baseline
The following data has been verified from live Shopify Admin API and GMC product exports:
| Metric | Value | Source | Date Verified |
|---|---|---|---|
| Total Shopify products | 5,582 | Shopify Admin API | 2026-02-10 |
| Active products | 2,425 | Shopify Admin API | 2026-02-10 |
| Archived products | 3,121 | Shopify Admin API | 2026-02-10 |
| Draft products | 36 | Shopify Admin API | 2026-02-10 |
| Unique product types | 90 | Shopify Admin API | 2026-02-10 |
| Unique vendors | 175 | Shopify Admin API | 2026-02-10 |
| Unique tags | 2,522 | Shopify Admin API | 2026-02-10 |
| GMC total variants | 124,060 | GMC TSV export | 2026-02-10 |
| Active variants (est.) | ~15,000-20,000 | Calculated | 2026-02-10 |
| Avg variants per product | ~3.5 | Calculated | 2026-02-10 |
DR-02: GMC Custom Label Availability
| Label | Current State | Products Using | Available |
|---|---|---|---|
custom_label_0 | “Argonaut Nations - Converting” | 7 of 124,060 (0.006%) | Yes (safe to overwrite) |
custom_label_1 | Empty | 0 | Yes |
custom_label_2 | Empty | 0 | Yes |
custom_label_3 | Empty | 0 | Yes |
custom_label_4 | Empty | 0 | Yes |
DR-03: GMC Custom Label Constraints
| Constraint | Value | AdPriority Impact |
|---|---|---|
| Labels available | 5 (label_0 through label_4) | Full schema fits |
| Max characters per label value | 100 | All values under 30 characters |
| Max unique values per label | 1,000 | Max unique values used: ~20 (category group) |
| Total unique values (all labels) | 5,000 | ~38 total unique values |
| Case sensitivity | Not case-sensitive | Use lowercase for consistency |
| Shopper visibility | Not visible (internal only) | No customer-facing impact |
DR-04: Google Sheets Constraints
| Constraint | Value | AdPriority Impact |
|---|---|---|
| Maximum cells per spreadsheet | 10,000,000 | 6 columns x 20,000 rows = 120,000 cells (1.2%) |
| Maximum rows per sheet | 5,000,000+ | 20,000 rows (well within limit) |
| Maximum columns per sheet | 18,278 | 6 columns used |
| Sheets API write quota | 300 requests per minute | Batch writes handle full catalog in 1-2 requests |
DR-05: Product ID Format Verification
| Component | Format | Example | Verified |
|---|---|---|---|
| Full GMC product ID | shopify_US_{productId}_{variantId} | shopify_US_8779355160808_46050142748904 | Yes (124,060 products) |
| Item Group ID | {productId} | 8779355160808 | Yes |
| Country code | US for all Nexus products | US | Yes |
| All IDs are variant-level | No product-only IDs exist in GMC | – | Yes |
2.8 Acceptance Criteria
MVP (Phase 0: Nexus Implementation)
| # | Criterion | Verification Method |
|---|---|---|
| AC-01 | Priority scores assigned to all 2,425 active Nexus products | Database query: SELECT COUNT(*) WHERE priority_score IS NOT NULL |
| AC-02 | Supplemental feed (Google Sheet) created and connected to GMC | GMC dashboard shows feed with 0 errors |
| AC-03 | Custom labels populated on all active variants in GMC | GMC product search shows labels on random sample of 20 products |
| AC-04 | PMAX campaigns restructured by priority score | Google Ads dashboard shows asset groups filtered by custom_label_0 |
| AC-05 | 30-day baseline ROAS documented before change | Spreadsheet with daily ROAS data for 30 days pre-deployment |
| AC-06 | Process documented for SaaS replication | Step-by-step guide covers: category mapping, scoring, feed generation, GMC connection |
Beta Release (Phase 1)
| # | Criterion | Verification Method |
|---|---|---|
| AC-10 | Shopify app installable via OAuth | Test install on development store completes without errors |
| AC-11 | OAuth token exchange flow working | Session token validated, API requests succeed |
| AC-12 | Product import from Shopify working | Import 5,000 products in < 5 minutes |
| AC-13 | Basic priority UI functional | Merchant can view, search, filter, and edit product priorities |
| AC-14 | Manual GMC sync (Google Sheets) working | Click “Sync Now” updates Sheet within 60 seconds |
| AC-15 | GDPR webhooks implemented | All 3 mandatory endpoints return 200 OK |
| AC-16 | 5 beta testers onboarded | 5 real merchants have installed and used the app |
Production Release (Phase 2-3)
| # | Criterion | Verification Method |
|---|---|---|
| AC-20 | All subscription tiers functional | Test purchase on each tier succeeds |
| AC-21 | Automated GMC sync on schedule | Sync runs at configured interval without manual intervention |
| AC-22 | Seasonal calendar auto-transitions | Simulated season boundary triggers recalculation |
| AC-23 | Rules engine UI complete | Merchant can create, edit, test, and delete rules without code |
| AC-24 | New arrival boost working | Product created today shows priority 5 in database |
| AC-25 | Shopify Billing API integrated | 14-day trial starts on install, converts to paid plan |
| AC-26 | App Store listing approved | Published and discoverable on Shopify App Store |
| AC-27 | Onboarding flow complete | New merchant reaches first sync within 30 minutes |
2.9 Constraints and Assumptions
Technical Constraints
| Constraint | Value | Impact on AdPriority |
|---|---|---|
| GMC custom label character limit | 100 characters per value | All AdPriority values are < 30 characters |
| GMC unique values per label | 1,000 maximum | AdPriority uses ~38 total unique values |
| GMC product update limit | 2x per day per product via Content API | Daily sync schedule is sufficient |
| Google Sheets cell limit | 10,000,000 cells | Even at 124K variants x 6 columns = 744K cells (7.4%) |
| Shopify API rate limit | 40 requests/second (REST), 1,000 points/second (GraphQL) | Paginated import at 250 products/page stays within limits |
| Shopify metafield value limit | 512KB per metafield | AdPriority JSON payload is < 1KB |
| Shopify webhook delivery | At-least-once (may deliver duplicates) | Idempotent webhook handlers required |
Platform Constraints
| Constraint | Impact |
|---|---|
| Shopify requires embedded apps for new App Store submissions | Must use App Bridge v4+ and Polaris UI |
| Shopify deprecated cookie-based authentication | Must use session tokens (JWT) |
| GDPR webhooks are mandatory | Three endpoints must exist even if no customer data is stored |
| Shopify takes 20% revenue share on app subscriptions | Pricing must account for this margin |
Assumptions
| Assumption | Risk if Wrong | Mitigation |
|---|---|---|
| Merchants have a Google Merchant Center account with active product feed | App is useless without GMC | Onboarding check verifies GMC account exists |
| Shopify Google Channel creates the primary product feed | AdPriority adds supplemental labels only | Documentation explains this prerequisite |
| Merchants understand the concept of priority-based budget allocation | Low adoption if concept is unclear | Onboarding tutorial, help docs, video walkthrough |
| ROAS will improve with proper product segmentation | Core value proposition fails | Phase 0 validates with Nexus before building SaaS |
| Google Ads PMAX adoption continues growing | Market shrinks if PMAX declines | Monitor Google Ads product announcements |
2.10 Out of Scope (v1.0)
The following features are explicitly excluded from version 1.0 and may be considered for future releases:
| Feature | Reason for Exclusion | Potential Future Version |
|---|---|---|
| Multi-channel support (Facebook Ads, Microsoft/Bing Ads) | Adds complexity without validated demand; Google Ads is the primary channel | v2.0+ |
| Automated bid management | AdPriority controls priorities, not bids; bid management is a separate product category | Not planned |
| Creative optimization (ad copy, image selection) | Outside core scope of product prioritization | Not planned |
| Inventory-based automation (auto-exclude when stock < N) | Partially addressed by tag-based rules; full inventory integration deferred | v1.5 |
| Machine learning predictions (demand forecasting, score suggestions) | Requires significant training data from multiple merchants | v2.0+ |
| Mobile app | Embedded Polaris app works on mobile Shopify admin | Not planned |
| Variant-level priority overrides | Product-level scoring is sufficient for v1.0; variant overrides add complexity | v1.5 |
| Multi-currency support | Nexus is US-only; international stores may need localized pricing | v2.0+ |
| White-label / API access | Enterprise feature; requires additional infrastructure | v2.0+ (Enterprise tier) |
| Campaign creation/management | AdPriority provides labels, not campaign management; merchants structure PMAX themselves | Not planned |
2.11 Open Questions and Risks
Open Questions
| # | Question | Status | Resolution Path |
|---|---|---|---|
| OQ-01 | Variant-level vs. product-level priorities for v1.0? | Resolved: Product-level for v1.0, variant-level for v1.5 | Variant-level in GMC feed (required), but priority is set at product level |
| OQ-02 | Optimal sync frequency vs. API rate limits? | Resolved: Daily for Google Sheets (GMC fetches daily anyway) | Content API (Pro) stays within 2x/day/product limit |
| OQ-03 | How to handle products not in GMC feed? | Open | Reconciliation job identifies unmatched IDs; exclude from supplemental feed with logged warning |
| OQ-04 | What GMC custom label slots are already in use for Nexus? | Resolved: All 5 available (7 products on label_0, negligible) | Safe to overwrite |
| OQ-05 | Pricing validation with target market? | Open | Validate during beta with 5 testers; compare to competitor pricing |
| OQ-06 | How to handle stores with > 100K variants in Google Sheets? | Open | Sheet can handle 744K cells (7.4% of limit); Content API fallback for extreme cases |
| OQ-07 | Should seasonal transitions happen at midnight UTC or merchant timezone? | Resolved: Merchant timezone | Timezone stored in store settings |
Risks
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| ROAS does not improve with priority-based segmentation | Medium | Critical – invalidates premise | Phase 0 validates with real store before SaaS investment |
| GMC feed processing delays exceed 24 hours | Low | Medium – delayed label application | Use manual “Update” trigger in GMC; monitor processing times |
| Shopify API rate limits block large imports | Medium | Medium – slow initial setup | Implement pagination, respect rate limit headers, use bulk GraphQL |
| Google Sheets API quota exceeded during bulk sync | Low | Medium – sync failure | Batch writes, exponential backoff, manual CSV fallback |
| Competitor copies the priority scoring approach | Low | Medium – reduced differentiation | First-mover advantage; build brand; add features competitors cannot easily replicate |
| Shopify App Store rejection | Medium | High – blocks launch | Study requirements thoroughly; test against all criteria before submission |
| Seasonal transition applies wrong priorities | Medium | High – wrong products advertised | Preview mode before applying; manual override; monitor 48 hours after transition |
| Merchants find the rules engine too complex | Medium | High – adoption friction | Pre-built templates; wizard-based onboarding; start simple, add complexity gradually |
2.12 Requirement Traceability Matrix
This matrix maps each functional requirement to its implementation phase, source document, and related acceptance criteria.
| Requirement ID | Description | Phase | Source | Acceptance Criteria |
|---|---|---|---|---|
| FR-01 | Priority Score Definition | 0 | requirements.md | AC-01 |
| FR-02 | Score Assignment Methods | 0-3 | requirements.md | AC-13 |
| FR-03 | Score Override Hierarchy | 1 | category-mapping.md | AC-13 |
| FR-10 | Category Rules | 1 | category-mapping.md | AC-13, AC-23 |
| FR-11 | Rule Examples | 1 | category-mapping.md | AC-23 |
| FR-20 | Product Identification | 0 | product-mapping.md | AC-02, AC-03 |
| FR-21 | Custom Label Structure | 0 | product-mapping.md, google-merchant-center-api.md | AC-03 |
| FR-30 | Season Definitions | 2 | seasonal-calendar.md | AC-22 |
| FR-31 | Seasonal Rules | 2 | category-mapping.md, seasonal-calendar.md | AC-22 |
| FR-40 | New Arrival Boost | 2 | requirements.md | AC-24 |
| FR-50 | Sync Methods | 0-2 | google-merchant-center-api.md | AC-02, AC-14, AC-21 |
| FR-51 | Sync Schedule | 1 | requirements.md | AC-14, AC-21 |
| FR-52 | Error Handling | 1 | requirements.md | AC-14 |
| FR-60 | Performance Data | 3 | google-ads-api.md | – |
| FR-61 | Recommendations | 3 | google-ads-api.md | – |
| IR-01 | App Installation | 1 | shopify-app-requirements.md | AC-10, AC-11 |
| IR-02 | Data Access | 1 | shopify-app-requirements.md | AC-12 |
| IR-03 | Webhooks | 1 | shopify-app-requirements.md | AC-15 |
| IR-10 | GMC Authentication | 0-2 | google-merchant-center-api.md | AC-02 |
| IR-11 | GMC API Operations | 2 | google-merchant-center-api.md | AC-21 |
| IR-20 | Google Ads Authentication | 3 | google-ads-api.md | – |
| IR-21 | Google Ads API Operations | 3 | google-ads-api.md | – |
Summary
This Product Requirements Document defines 21 functional requirements, 24 non-functional requirements, 11 integration requirements, and 5 UI/UX requirement areas for AdPriority. Every requirement traces back to the core problem: retailers waste ad spend by treating all products equally in Google Ads.
The requirements are organized by priority and phase:
- Phase 0 (Week 1-2): Core scoring system (FR-01, FR-20, FR-21, FR-50) validated with Nexus Clothing
- Phase 1 (Week 3-4): SaaS foundation (FR-02, FR-03, FR-10, FR-51, FR-52, IR-01 through IR-03) with 5 beta testers
- Phase 2 (Week 5-8): Full product (FR-11, FR-30, FR-31, FR-40) with seasonal automation and rules engine
- Phase 3 (Week 9-12): App Store launch (FR-60, FR-61, IR-20, IR-21) with billing and scale
Key technical decisions validated during the research phase:
- GMC product ID format:
shopify_US_{productId}_{variantId}– confirmed from 124,060 product export - All 5 custom labels available: Only 7 products use label_0 (0.006%), labels 1-4 are empty
- Supplemental feed pipeline works: 10/10 test products matched in GMC with zero errors
- Google Sheets capacity is sufficient: ~120,000 cells for Nexus (1.2% of 10M limit)
The next step is to use these requirements as the implementation guide for Phase 0 (Chapter 18) and the architectural foundation for the system design (Chapter 4).