Black Friday Readiness: Checks

Download skillThe skill is a reference md and part of wix-manage skill. You can use the following command to add the full wix-manage skill to your project:
Copy

Every endpoint and field below was verified against a live Wix Stores (Catalog V3) site. Use them as written. For anything not here, confirm the method in the Wix REST docs first.

All calls are site-scoped (the site's siteId on the call that runs the function) except the homepage fetch, which is scope: "account" against the public site URL: that one request is signed with the account's credentials rather than the site's, so a reader making the calls one by one uses an account-level token for it and the site token for everything else.

Probe 1 — read everything (no mutations)

Run once, and send the function as it is written here. It contains no backslashes on purpose: a backslash that gets doubled on the way to the executor breaks a regular expression and the whole call. If a run still fails to parse, fix only the token the error names and send it again; do not rewrite the function or drop reads, because the pass rules below read its exact output. Each probe is independent; a failure is returned as { ok: false, error } for that probe only. Replace the placeholders: the first two from the site context, the sale day and sale name from step 2 of the review. A site that is not published has no URL: set SITE_URL to an empty string and still send the whole function. Only homepage and pages come back empty, stockSummary and testProduct are still returned, and checks 4–8 become checklist lines to look at once the site is live.

Copy

What each probe returns (verified fields)

ProbeEndpointFields used
productsPOST /stores/v3/products/query with fields: ["PLAIN_DESCRIPTION","URL","MERCHANT_DATA","DIRECT_CATEGORIES_INFO"]id, name, slug, visible, productType, media.main, actualPriceRange.minValue.amount (string), compareAtPriceRange (absent when none), plainDescription (HTML), brand.name (absent when none), costRange.minValue.amount ("0" when no cost entered), ribbon.name, inventory.availabilityStatus, variantSummary.variantCount, url.url, directCategoriesInfo.categories[].id (only with DIRECT_CATEGORIES_INFO; inventory items also carry product.directCategoryIds). 100 per page, pagingMetadata.hasNext / cursors.next; keep fields on every page.
testProductcomputed in the probe{ id, name, price, compareAt, singleVariant }, or null when nothing can be bought: the product every Probe 2 cart uses
stockSummarycomputed in the probe from products and inventoryvisibleProducts, canBeBought, outOfStock[] and unclear[] (each { name, why }), stockCountingOff[] (names), inventoryRead. A tracked row counts as in stock when quantity > 0; an untracked one when inStock is true. A product is out of stock only when its inventory rows and its own availability agree; when they disagree it is in unclear.
inventoryPOST /stores/v3/inventory-items/query, limit up to 1000one item per variant: productId, variantId, trackQuantity, quantity (tracked only), inStock (untracked only), availabilityStatus, product.name, product.variantName, product.directCategoryIds. Next page while pagingMetadata.cursors.next exists.
categoriesPOST /categories/v1/categories/query with treeReference.appNamespace: "@wix/stores", returnNonVisibleCategories: truecategories[].{id, name, slug, visible, itemCounter, parentCategory.id}. Catalog V3 only.
couponsPOST /stores/v2/coupons/query, query.paging.limit 100`coupons[].specification.{name, code, active, startTime, expirationTime (epoch ms), usageLimit, limitPerCustomer, minimumSubtotal, scope, percentOffRate
discountRulesPOST /ecom/v1/discount-rules/query`discountRules[].{id, revision, name, active, activeTimeInfo.{start,end} (ISO), discounts.values[].{discountType, percentage
shippingOptionsPOST /ecom/v1/shipping-options/queryshippingOptions[].{title, deliveryRegionIds, estimatedDeliveryTime, rates[].{amount, conditions[], multiplyByQuantity}}. A rates[].conditions entry with a subtotal bound is the free-shipping threshold.
deliveryProfilesPOST /ecom/v1/delivery-profiles/query body {}deliveryProfiles[].deliveryRegions[].{id, name, active, destinations[].countryCode}. A region with destinations: [] is "rest of world".
checkoutSettingsGET /ecom/v1/checkout-settingscheckoutSettings.checkoutPolicies.{termsAndConditions, privacyPolicy, returnPolicy, digitalItemPolicy, contactUs, customPolicy}.{visible, content} (customPolicy also title); checkoutFields.policyAgreementCheckbox.visible
marketingTagsGET /marketing/v1/tagstags[], each with enabled and one tag object (googleAnalytics, facebookPixel, googleTagManager, googleAds, tikTokPixel, yandexMetrica) holding trackingId. Empty tags: [] = nothing connected.
automationsPOST /automations-service/v2/automations/queryautomations[].{id, name, origin, configuration.status ("ACTIVE" / "INACTIVE"), configuration.trigger.triggerKey}
campaignsGET /email-marketing/v1/campaigns?paging.limit=100&paging.offset=Ncampaigns[].{campaignId, title, visibilityStatus (DRAFT / PUBLISHED / TEMPLATE), distributionStatus (SCHEDULED, DISTRIBUTED, SENDING, NOT_STARTED, PAUSED, …), publishingData.datePublished, dateUpdated}. No date filter, no hasNext: page by offset until a page has fewer than 100.
giftCardProductsPOST /gift-cards/v1/gift-card-products/querygiftCardProducts[].{id, name, presetVariants, customVariant}. Empty = the store sells no gift card (a site holds at most one).
cookieBannerGET /wixpress/cookie-banner-settings/v1/cookie-banner-settingssettings.enabled. A 400 "No application found for the requested site" means the cookie-banner app is not installed: treat as banner off, not as ❔.
sitePropsGET /site-properties/v4/propertiesproperties.timeZone, properties.paymentCurrency, properties.locale.country
staticPagesPOST /ricos/v1/site-context/static-pages/list body {}items[].{title, url}. Returns the home page and unmanaged pages; policy pages built in the editor appear here, app pages don't. Ignore its top-level published flag (it reads false on published headless sites).
pagesGET <sale category URL> and GET <discounted product URL> at scope: "account"pages.saleCategory.{url, ok, textStart}, pages.discountedProduct.{url, ok, showsBothPrices} (both the compare-at and the sale amount appear in the page text). URLs are resolved from the homepage's own hrefs by slug, because a headless site's routes differ from the catalog's url field (which 404s there); the catalog URL is the fallback. These URLs are what the fallback checklist links; ok: false → link the homepage instead and say "find from the shop page".
homepageGET <site URL> at scope: "account" (the call that runs the function must carry this site's siteId; only the account's own published sites can be fetched, a 403 … is not a site of this account means the wrong site id or an unpublished site)raw HTML, scripts and styles stripped before reading. Editor sites and most headless sites carry their text, nav and links server-side; linkedProducts and linkedCategorySlugs match each product's and category's own URL path, so they work on both. Under 300 characters of real text is a JS shell: clientRendered: true.
ordersPOST /ecom/v1/orders/search on purchasedDate, page by metadata.cursors.nextorders[].{status, priceSummary.total.amount, lineItems[].{catalogReference.catalogItemId, productName.original, quantity}}. Exclude status: "CANCELED". (priceSummary.total is per the Orders docs; the test store had no orders to exercise it.)
paymentMethodTypes (optional)GET /payments/site-payment-method-types/v1/site-payment-method-types?countryCode=<cc>&currencyCode=<cur>sitePaymentMethodTypes[].{legacyId (creditCard, payPal, applePay, googlePay, klarna, …), displayName}. Lists what is available in the country, not what the store has turned on. Use only to word check 3's checklist item.

Probe 2 — test carts (creates and deletes temporary carts)

Run after Probe 1, once, with hasMutations: true. The product is testProduct from Probe 1, in every cart: do not choose another. Name it and its prices in the report ("BF20 takes Oak Candle from $24.00 to $19.20"). When testProduct.singleVariant is false, use GET /stores/v3/products/{id} and take variantsInfo.variants[0].id as catalogReference.options.variantId. When testProduct is null, skip Probe 2. Build one scenario per delivery region (one destination country from destinations[].countryCode; a region with destinations: [] is "rest of world", so pick a country no other region covers, e.g. DE when the only other region is US) × both sides of any free-shipping threshold (quantity chosen so the subtotal lands above and below it) × each active coupon code. Every coupon scenario has a twin without the coupon (same product, quantity and region), so the automatic discount alone and the coupon on top of it can be compared (checks 17, 18). Send the function as written and change only the scenarios list. Carts are deleted in finally.

Copy

Verified behavior: Create Cart with a coupon code that does not exist, is inactive, or has not started fails with INVALID_COUPON_CODE and creates no cart. Calculate returns { cart, summary }; summary.deliverySummary holds the auto-selected method and its price for the address; MISSING_DELIVERY_METHOD is always present on a cart without an explicit selection and is noise; any other violation (no shipping to the region, item unavailable) is a finding. priceSummary.discount.amount may come back as "0" while the other amounts carry decimals: compare numerically.

Probe 3 — look at the live site (browser pass)

Run after Probes 1 and 2 whenever the agent has a tool that opens web pages and returns what they look like (a browser tool, a screenshot tool, a page-reading tool with rendered text). An agent with only API access and no page-opening tool has none: skip to the fallback.

Pages, in order. All on the merchant's live site; URLs come from Probe 1.

#PageURLLook for
P1Homepage, desktopSITE_URL4 the announcement bar: visible without scrolling, its text and dates · 5 featured products: click or hover each, note name · 7 a countdown: its end time and what it counts to · 8 the menu: a link to the sale category · 10 footer: links to returns, shipping, terms, privacy · 12 the cookie banner appears on first visit (do not click it)
P2Homepage, phone width (≈ 390 px) when the tool can resizeSITE_URL4 bar still visible and readable · 8 the sale link is in the mobile menu · nothing overlaps the hero
P3Sale category pagepages.saleCategory.url8 it exists and lists products · 6 each product card shows the crossed-out original and the sale price · 5 out-of-stock badges
P4One discounted product pagepages.discountedProduct.url6 crossed-out original price beside the sale price · stock message · the add-to-cart button works
P5Cart, after adding that productfrom P49 a shipping estimate or "calculated at checkout" · the coupon field exists · 1 totals readable
P6Checkout, stopped before paymentfrom P59 shipping options and prices for the pre-filled or default region · 10 policy links in the checkout footer · 12 the "I agree" checkbox when policies exist · the payment methods offered (3): cards, PayPal, Apple Pay / Google Pay, buy-now-pay-later

Rules. Stop at the payment step: never enter card details, an email, a name, an address or any personal data; never submit a form; never log in; never accept or decline the cookie banner. The item added to the cart in P5 is removed afterwards when the tool allows; otherwise it is a visitor cart that expires on its own and nothing is ordered. Take a screenshot of each page when the tool can, and name it in the evidence ("homepage-mobile.png"). Spend at most one attempt per page; a page that does not load is ❔ for its checks, not a retry loop.

What P1–P6 change in the grading. A visual check seen in the browser is graded from what was seen, and the evidence line says so ("the bar reads 'Black Friday 30% off Nov 27–30', matches your discount"; "Oak Wick Trimmer shows $34 crossed out, $24 in red"). A visual check contradicted by the browser overrides the HTML probe (the bar's text is in the HTML but hidden behind a popup → ⚠️). Check 3 gains real evidence from P6: the payment methods shown at checkout.

Fallback when there is no browser. Say so in the report header. Then the checklist opens with the group On your live site, one checkbox per visual check the agent could not see, each with the exact URL and the thing to look at, on a phone and on a computer:

  • Open <SITE_URL> on your phone. Check that the bar shows without scrolling and says . (4)
  • Open each featured product on your homepage. Check that you can add it to the cart. (5)
  • Open <pages.discountedProduct.url>. Check that is crossed out next to . (6; omit when showsBothPrices is true and say "the page shows both prices; check that the original one is crossed out")
  • If you have a countdown, check that it ends at <discount end, store time zone> and shows "Sale ended" at zero. (7)
  • Check that is in your site menu, on your phone too. (8; omit when linkedCategorySlugs holds its slug and visible is true, say "it's linked; check that it shows on your phone")
  • Add one item to the cart and go to checkout, without paying. Check that shipping options and prices appear for
    . (1, 9)
  • On the checkout page, check that your return policy, terms and privacy policy are linked. (10)
  • On the checkout page, check that Apple Pay, Google Pay and PayPal are offered. (3)
  • Open your site in a private window. Check that the cookie banner shows. (12; omit when the banner API said enabled: true and the store does not ship to the EU/UK)

HTML evidence from Probe 1 only trims this list (a check whose HTML signal is unambiguous gets the shorter "glance" wording); it never removes the group.

Pass rules per check

Thresholds are defaults; the merchant can override them in chat.

Marks: ✅ passes. ⚠️ is a verified problem: it goes in 🔧 I can fix these when the rule's Offer names an API change the agent can make, and in 📋 Your checklist (✋) when only the merchant can act. A rule's ✋ line is always a checklist item. ❔ when the probe failed or the data is not exposed. ➖ not applicable. Convert every time to the store time zone (siteProps.properties.timeZone) before stating it; coupon times are epoch ms, rule times ISO.

The sale window (used by 4, 7, 11, 16, 19, 21) is the union of the windows of active coupons and discount rules that overlap the sale week: the 7 days before the sale day through the sale day. For Black Friday (the Friday after US Thanksgiving, the fourth Thursday of November) the week extends through the following Tuesday, to cover Cyber Monday. No such window → "no dated sale found yet".

Any sale, not only Black Friday. In every line the owner reads, <sale name> is the sale from step 2 of the review ("Christmas", "Mother's Day", "Black Friday"); never print "Black Friday" for another sale. "The campaign start" (checks 21, 23) is 26 days before the sale day, which is Nov 1 for Black Friday 2026. ordersLastBfcm holds the same sale's weeks one year earlier, whatever the sale. Anything about Cyber Monday applies to Black Friday only.

Stock facts come from stockSummary. "N of M products can be bought", and which products are out of stock, are read from stockSummary, never worked out again from the product and inventory lists: a tracked row has a quantity and no inStock field, so a missing inStock does not mean out of stock. Call a product out of stock only when it is in stockSummary.outOfStock, and give its why in plain words ("counted stock is 0"). When that list is not empty, the count in the header is not enough: 🔧 has its own line under "Needs one answer from you" that names up to three of them, "2 products are out of stock: Wick Trimmer, Gift Box. Give me the quantities and I'll set them, or tell me to hide them." It starts with "Stops sales." when no product can be bought. A product in stockSummary.unclear is never called out of stock: it becomes a checklist line, "Check that can be bought. Its stock record and its product page disagree." When stockSummary.inventoryRead is false, say stock could not be checked.

Truncated lists. When truncated.<list> is true, every check that reads that list (inventory: 5, 13, 15; coupons: 16, 17, 19, 20, 21; discountRules: 16, 18, 19, 21; shippingOptions: 9) reports ❔ "more than N items; I checked the first N" instead of ✅, and the fix offers stand only for items actually read.

A. Checkout, payments & catalog

1 Test cart prices — ✅ when every Probe 2 scenario is ok with no violations and total = subtotal − discount + delivery + tax. ⚠️ (agent-fixable only when the cause is a shipping or discount setting; otherwise ✋) on any violation or arithmetic mismatch, quoting the scenario. ✋ always: "Place one real order on your live site and refund it; confirm the confirmation email arrives." When every scenario returns tax of 0 and the store ships to a taxed region (US, EU, UK, CA, AU): ✋ "Tax came to 0 on test carts; confirm your tax settings" → ecom-platform/tax. ➖ when no purchasable product.

2 Product completeness — over visible: true products: ⚠️ list those with hasImage === false, price <= 0 or NaN, or descriptionChars < 20. ✅ when none. Offer: write the missing descriptions (merchant approves the text) → 🔧; images and prices → ✋.

3 Payments and payment methods — Browser: P6 shows which methods are offered at checkout; ✅ when cards plus PayPal, Apple Pay or Google Pay, and a pay-later option appear, ⚠️ → ✋ naming the missing ones. API: no public read exposes the provider's status or which methods are on, and a test cart never reaches payment. Always the first ✋ under "In your dashboard": "Confirm payments are connected and live (not test mode), and turn on Apple Pay, Google Pay, PayPal and a pay-later option such as Klarna or Afterpay — 12% of customers abandon when their method is missing" → wix-cashier/payments. Optionally name which of those are available in the store's country from paymentMethodTypes[].legacyId. A store with no orders ever also gets this in the verdict: "confirm payments are connected before anything else".

B. Sale merchandising (live homepage)

With a browser (Probe 3), checks 4–8 are graded from what was seen on P1–P4; the HTML signals below are the fallback and the tie-breaker. Without a browser: grade from the HTML signals where they are unambiguous, and every check in this section also appears in the On your live site checklist group with the "glance" wording. When homepage.ok is false or clientRendered is true and there is no browser, checks 4, 5, 7 and 8 are their full checklist lines and nothing is graded ✅; check 6 still runs from the catalog.

4 Announcement bar — Browser: ✅ when the bar is visible without scrolling on desktop and phone with the sale's dates; ⚠️ → ✋ when hidden, missing, or the dates differ. HTML: ✅ only as "text present" when saleMentions is non-empty AND a date in datesMentioned falls inside the sale window, with the glance line in the checklist. ⚠️ → ✋ when the sale window exists but the page has no sale mention, or the dates differ ("your bar says Nov 25–30, your discount runs Nov 27–Dec 1"). ✋ also: "Confirm the bar is scheduled to come down on in the editor." ➖ when no dated sale.

5 Hero product links in stock — Browser: each featured product opened from P1 shows in stock. HTML: over linkedProducts: ⚠️ list any whose availability is OUT_OF_STOCK or whose tracked inventory sums to 0; swapping the featured product → ✋; restocking with quantities the merchant gives → 🔧. ✅ otherwise. ➖ when the page links no products.

6 Crossed-out original price — Browser: P3 and P4 show the original struck through beside the sale price. HTML: pages.discountedProduct.showsBothPrices true is "both prices present" (glance line stays). Catalog: candidates: products targeted by an active discount rule, with ribbon matching /sale|black friday|deal|holiday/i, or with compareAt set. ⚠️ → 🔧 when a candidate has no compareAt, or compareAt <= price: set compare-at prices on approval. ✅ otherwise.

7 Countdown timer — Browser: the timer on P1 counts to the discount end; ⚠️ → ✋ when it ends at another time or shows negatives. HTML: only if hasCountdown. ✋ "Your timer should end at <discount end, store tz>; preview it with a past date to see what customers see at zero." ➖ when no timer found.

8 Sale category in the menu — sale category = a category whose name matches the sale's name or /sale|cyber|holiday|deal|gift/i (saleRe in the probe). Browser: ✅ when P1 and P2 show it in the menu (desktop and mobile) and P3 lists products. HTML: ✅ when one exists, is visible, has items > 0 and its slug is in linkedCategorySlugs (glance line for mobile stays). Exists but not linked → ✋ "Add to your site menu" (editor). Exists but hidden or empty → 🔧 show it / fill it from discounted products. None → 🔧 "create a category from your discounted products" (S6), and ✋ to add it to the menu afterwards.

C. Shipping

9 Shipping per region — Browser: P6 shows shipping options and prices for the default region; a checkout with no shipping option is ⚠️ regardless of what Probe 2 said. API: one Probe 2 scenario per active region; the subtotal threshold from shippingOptions[].rates[].conditions, one scenario each side. ✅ when every region returns a deliveryMethod and delivery flips to 0 only above the threshold. ⚠️ → 🔧 when a region has no method, a free-shipping rate applies below the threshold, or a region has no shipping option (Shipping dispatcher recipe). Separately ⚠️ → 🔧 when a shipping option has no estimatedDeliveryTime: "customers see no delivery estimate at checkout" → add it once the merchant gives transit times. Worldwide free shipping with no minimum → ✋ "confirm this is intended for your price range" → ecom-platform/delivery-profile.

10 Shipping / returns / refund policy — Browser: footer links seen on P1 and policy links seen on P6 count as the "linked" half. HTML/API: ✅ when returnPolicy.visible && content.length > 50 AND (footerPolicyLinks has a return/shipping/refund link OR staticPages has a page titled like one). Checkout policy hidden or empty → 🔧 draft and enable it on approval. Footer link missing → ✋ "link your returns and shipping policy in the site footer"; on a clientRendered page that half is ✋ "confirm the footer links to your policies". ✋ always: "Read the wording; is it still current?"

11 Sale terms — ✅ when customPolicy.visible with content mentioning the sale, or a static page / footer link titled like "sale terms" / "promotion terms". ⚠️ → 🔧 when a dated sale exists and none is found: draft the terms from the real discounts (dates, excluded scopes, whether the coupon stacks with automatic rules, final-sale items) into customPolicy. ➖ when no dated sale (check 16's fix drafts them together).

12 Terms, privacy, cookie banner — Browser: the banner appearing on P1 (first visit) is the banner's evidence, and the "I agree" checkbox seen on P6 is the checkbox's. API: ✅ when termsAndConditions.visible and privacyPolicy.visible with non-empty content AND cookie banner settings.enabled. Missing policy text → 🔧 (Update Checkout Settings). Banner off or app missing → ✋ "install and switch on the cookie banner in your site settings"; it is a ✋ problem when the store ships to the EU/UK (a region covering an EU country or rest-of-world) or locale.country is in the EU, otherwise a plain ✋ confirmation. When both policies exist but policyAgreementCheckbox.visible is false → 🔧 "show the 'I agree to the terms' checkbox at checkout". ✋ always: "Confirm the legal wording is right for you (GDPR for EU customers, CCPA for US)."

E. Stock

13 Stock vs demand — demand per product = max(units in ordersLastBfcm, 2 × units in orders30d). Candidates: products targeted by the sale discount, plus the top 5 by last-year units. ⚠️ → ✋ when tracked stock (sum of quantity over its inventory items) < demand: ": N in stock, ~M expected. Decide what to reorder". ✅ when none. ❔ when both order sets are empty or skipped ("no sales history yet to size demand; with N units of X in stock, decide yourself").

14 Low-stock alerts — not readable by API. ✋ "Check low-stock notifications are on in your store settings" → wix-stores/settings.

15 Inventory tracking — ⚠️ → 🔧 list physical products with any inventory item tracked === false ("sold as unlimited"): turn tracking on once the merchant gives quantities. ✅ when none.

Costs — when every product has cost of 0 or null, add one ✋ under "In your dashboard": "Enter product costs so I can check that discounts never price anything below cost" → wix-stores/products. Check 18's below-cost rule is then skipped and says so.

F. Coupons & discounts

16 Sale discount exists with right dates — ✅ when at least one coupon or rule overlaps the sale week with both start and end set. ⚠️ → 🔧 when none exists ("Set up your discount", the first fix under "Needs one answer from you", asking for the offer and the dates); a coupon/rule has active: false; start is after the sale day; no end ("runs forever"); or a coupon and a rule meant for the same sale have different windows. A rule or coupon that passes is the first line of ✅ with its dates as they are ("25% off everything runs Nov 1 to Dec 31"): never propose other dates for it, however long it runs. The review's sale-dates section applies only when creating a discount.

17 Coupons work on a cart — one Probe 2 scenario per active coupon code. ✅ when discounts names the coupon and discount equals the expected amount (percentOffRate × subtotal, moneyOffAmount, or delivery 0 for freeShipping) and a cart below minimumSubtotal is rejected or undiscounted. ⚠️ → 🔧 on couponInvalid (not started, inactive, wrong code) or a wrong amount.

18 Coupon on top of the automatic discount — compare each coupon scenario with its twin without the coupon. The original price is the product's compareAt when set, else its price. ⚠️ → 🔧 when cost > 0 and the unit price after both discounts < cost ("drops below your cost of X"), or when the two together take more than 50% off the original price without the merchant having said so: "BF20 adds to your automatic 25%: Oak Candle is $34, $18.00 with the sale and $14.40 with the coupon too. I'll switch BF20 off while the sale runs, or end it before the sale starts, or turn it into part of the automatic discount. Which one?" ✅ otherwise. ➖ when no automatic rule. Cost checks only where cost > 0.

18 and 19 are two findings and two numbered fixes, never one "stacking" line: 18 is the coupon adding to the rule, 19 is the sale reaching products that are already marked down. Each names its products and amounts and ends with the change the agent will make.

19 No stacking on already-discounted products — already-discounted = products with compareAt > price. ⚠️ → 🔧 when an active sale rule targets the whole catalog (a scope with catalogItemFilter and no product ids) or a category containing them, or a sale coupon has scope.namespace: "stores" with no group, while already-discounted products exist: "N products already on sale (Oak Wick Trimmer $24 was $34) would get the extra X% — narrow the rule to exclude them, or set their compare-at as the sale price". ✅ when no overlap. ➖ when no sale rule or coupon.

20 Coupon usage limits — ⚠️ → 🔧 list active coupons with usageLimit or limitPerCustomer missing: set limits on approval (merchant picks the numbers; default suggestion per customer 1, total 3 × last year's order count). ✅ when all have both.

21 Everything scheduled — ✅ when every sale coupon and rule has both a start and an end (so nothing needs a manual switch), and at least one campaign has distribution: "SCHEDULED" or was DISTRIBUTED since the campaign start (26 days before the sale day). ⚠️ → 🔧 for a sale coupon or rule with active: false or no start ("needs you to switch it on by hand on the day") and for no scheduled campaign when a draft exists to schedule ("your emails depend on you pressing send"; with no draft it is ✋, see check 23). ✋ always when a dated sale exists: "Schedule the announcement bar and countdown in the editor to appear on and disappear on ."

G. Emails & tracking

22 Transactional automations — by trigger key: domain_events_wix.ecom.v1.automation_trigger-order_confirmation_issued_to_buyer (order confirmation), domain_events_wix.ecom.v1.automation_trigger-shipping_confirmation_issued (shipping confirmation), wix_e_commerce-cart_abandonment (abandoned cart). ✅ when each has at least one automation with status: "ACTIVE". ⚠️ → 🔧 list the INACTIVE or missing ones: activate (activation-status recipe) or build (build-and-manage recipe). Send history is not public: ✋ "Place the test order and check your inbox for the confirmation."

23 At least 4 emails for the sale — count every campaign with distribution: "SCHEDULED" (a scheduled send is in the future by definition) plus campaigns with distribution in {DISTRIBUTED, SENDING} whose published is on or after the campaign start (26 days before the sale day); never count by updated, it changes on every edit. ✅ when ≥ 4. ⚠️ when fewer: "you have N; stores with 4 or more saw +41% vs. the year before, 0 saw −20%". The agent does not create email campaigns, so the finding splits by what exists. Campaigns with visibility: "DRAFT" → 🔧 "I'll schedule your D drafts once you confirm each subject, audience and send time" (campaigns recipe). The emails still missing → ✋ "Write your emails: tease, early access, launch, reminder (and Cyber Monday for Black Friday)", link email-marketing/my-campaigns.

24 Tracking pixels — ✅ when tags has an enabled entry for Google Analytics (googleAnalytics) and for Meta (facebookPixel); TikTok (tikTokPixel) reported only if the merchant sells there. ⚠️ → 🔧 list the missing ones: connect once the merchant gives the tracking ID. ✋ "After your test order, confirm the purchase appears in GA / Meta."

25 Google Merchant Center connected — no public read (Search Console and Business Profile have connection APIs; Merchant Center does not). ✋ "Confirm your store is connected to Google Merchant Center. Google Shopping, Google's AI search and Google Ads all depend on it" → wix-stores/channel/google.

26 Product data ready for Google and AI search — over visible: true products: ⚠️ → 🔧 list those with nameChars < 15 or > 150, brand null, or descriptionChars < 50, ranked by last-year units then price: "fix titles, brands and descriptions on approval". ✅ when none. A store with no brand anywhere gets one line, not one per product.

27 Product feeds synced — not readable by API. ✋ "Check the Google and Meta channels for rejected products and that sale prices show" → wix-stores/channel/google.

H. Operations

28 Volume — one ✋ under "In your operations": "Expect roughly orders over the sale (the same week last year: N orders; last 30 days: M orders) → size packaging, labels and carrier pickups for that." When ordersLastBfcm.skipped is set, say "your store is newer than last year's " and use the 30-day figure only. ❔ only when both order probes failed.

Suggestions that read data (Part 2)

  • S14 gift cards: giftCardProducts empty → suggest creating one (gift-cards recipe); non-empty → suggest promoting it, link ecom-platform/gift-cards.
  • S15 free-shipping banner: threshold = round up orders30d.aov × 1.2 to a clean number in the store currency; ➖ when no AOV.
  • S7 / S8 feature picks and gift guide: from ordersLastBfcm.unitsPerProduct (fallback orders30d), skipping products with compareAt set (already discounted) and tracked stock under the 13-demand figure; tiers at the 33rd and 66th price percentiles of in-stock products.
  • S20 segments: counts come from orders (repeat vs one-time buyers, last year's holiday buyers absent in the last 30 days) and from the Contacts search recipe for signed-up-never-bought; labels only on approval.
  • S12 convert coupon to automatic discount: the discount-rule recipe, same window and scope as the coupon; deactivate the coupon only when the merchant says so.

Last updated: 10 November 2026

Did this help?