Skip to content

Analytics definitions

This is the semantic reference for Sellerlane analytics. It explains what each number counts, which clock owns it, and which dimensions can safely be combined. For money and ledger lifecycles, read Financial semantics.

Identity: session, visitor, and customer

These are three different units:

UnitMeaning
SessionOne storefront visit. It closes after 30 minutes without activity or at midnight UTC, whichever comes first. A new visit token creates a new session.
VisitorA distinct first-party browser identity (unique_token) within the selected range. One visitor can have many sessions. Cleared cookies or another device can create another identity; declining analytics tracking creates no reported visitor.
CustomerA durable customer account/contact identity. A visitor becomes linked to a customer only through a trusted sign-in, checkout, or identity-link event.

Session authority and boundary

The durable Valkey state—not the browser’s visit cookie and not a database lookup—is the session authority. State is isolated by a SHA-256 key over store, browser identity, and traffic class (human or bot); the raw browser identity is not present in the session key. A request reuses the current visit only when all of these are true:

  • the traffic class is unchanged;
  • the activity is on the same UTC calendar day; and
  • elapsed inactivity is strictly less than 30 minutes.

At exactly 30 minutes, and at 00:00:00Z, the next accepted activity receives a new visit token. The visit-cookie lifetime is the shorter of 30 minutes and the remaining time to midnight UTC. The cookie reflects the authoritative decision; it is never an independent fallback session resolver.

Document requests and validated /analytics/collect browser events are activity. A browser event first atomically touches or rolls the Valkey session and queues /visits/resolve, then queues the event behind that resolve in the same browser/store FIFO. A context-token refresh is only a heartbeat and does not extend the session. When an event starts a new session, its old page-view identifier is not joined across the session boundary.

Valkey selects the token and appends its seller resolve intent in one Lua transaction before Sellerlane exposes req.analytics or writes either identity cookie. Concurrent tabs therefore observe one new token. The seller’s trusted internal endpoint must create or touch that exact token: an unknown reuse token, a conflicting active token, or a token belonging to another browser/traffic class fails closed. Retrying the same create intent is idempotent and cannot increment the visitor’s session count twice.

If the Valkey result is uncertain, the buyer retries once with the identical task ID, intent ID, and candidate token; the Lua existing-task branch returns the committed decision. If neither attempt is observable, the request continues without analytics context or refreshed analytics cookies. Seller unavailability does not lose the accepted visit: its resolve remains durable and blocks later events for that browser until it succeeds.

These rules affect session counts, conversion denominators, bounce, landing pages, and attribution. They never create, remove, or change an order, payment, refund, tax, or sales-ledger amount; commerce facts remain authoritative even when analytics ingestion is delayed.

Staff, preview, and test traffic are excluded from merchant reports. Bot scope is part of each report definition rather than a global invariant. Market’s general traffic rollups are human-only, but the observed Shopify presets Sessions over time, Sessions by location, Sessions by referrer, and Conversion rate breakdown explicitly select both classified human and bot sessions. Those presets execute against retained visit-grain data and expose Human or bot session as a filter/dimension; other reports are not silently broadened.

Shopify labels newly classified sessions as human or bot and recommends a Human filter when bot traffic distorts conversion. Shopify’s documented classifier and filter apply only to data collected from 7 October 2025 onward and are not available for Headless/Hydrogen storefronts. Market retains its own auditable crawler/user-agent classification, so classification agreement with Shopify is an explicit reconciliation item—not an assumed identity.

For ranges up to 365 days, the overview counts distinct visitor identities over the whole range. It does not add daily distinct counts, which would count a visitor active on several days more than once. Longer ranges are explicitly labelled as a summed daily approximation.

Engagement and bounce

  • Page views count successfully recorded page-view events.
  • Session duration is the observed span from first activity to last activity. The 30-minute timeout is not added as engagement time.
  • A completed session bounces only when it recorded exactly one successful page view. A retained zero-pageview session is not a bounce; two or more page views are not a bounce.
  • Bounce rate is bounced sessions ÷ completed sessions for the selected scope. An active session is not labelled a bounce before it closes.

Web performance

The nine Core Web Vitals presets are three views—over time, by page type, and by page path—for each of LCP, INP, and CLS. They use validated browser Real User Monitoring samples only. Bot, staff, preview, test, server, and synthetic events are excluded.

Web performance is the deliberate exception to the normal store-timezone reporting clock. Its dates are UTC and both the primary and comparison ranges must fall within the latest 90 UTC calendar days. The server normalizes this family’s range to UTC even when a shared report form supplied the store timezone, and the resolved response reports UTC; the caller’s input object is not mutated. UTC is fixed report semantics here, not a selectable dimension.

P50, P75, and P95 are calculated from every individual retained experience in the selected range and grouping. Sellerlane does not average daily, device, page-type, or page-path percentiles; percentiles are not composable that way. The quality boundaries are:

VitalGoodNeeds improvementPoor
LCP≤ 2500 ms> 2500 ms and ≤ 4000 ms> 4000 ms
INP≤ 200 ms> 200 ms and ≤ 500 ms> 500 ms
CLS≤ 0.10> 0.10 and < 0.25≥ 0.25

The page-path dimension strips the URL authority, query string, and fragment, rejects protocol-relative or control-character values, caps the retained path, and groups it by SHA-256. This prevents tokens and customer data in URLs from entering merchant reports. A sample with no safe path can still contribute to the over-time and page-type populations, but not to a page-path row.

This proves the Sellerlane population calculation; it does not prove that Shopify collected the identical browser population or used an identical quantile implementation. The presets therefore remain executable-unreconciled until the controlled Shopify exports are captured and compared.

Conversion funnel

The funnel is session-based:

session → product view → add to cart → checkout started → contact → address → shipping → payment information → purchase

Each stage counts distinct qualifying sessions, not raw button clicks and not line-item quantity. The headline rates share the same denominator:

  • add-to-cart rate = sessions with add-to-cart ÷ sessions;
  • checkout-start rate = sessions with checkout start ÷ sessions;
  • conversion rate = converted sessions ÷ sessions.

Checkout completion rate is intentionally different: converted checkout sessions ÷ checkout-started sessions. A session that places two orders is still one converted session but can contribute two eligible orders to an order report.

Product recommendation conversion funnel

Shopify’s Product recommendation conversions over time report is a session funnel; raw impressions, clicks, order lines, or revenue are not valid substitutes. Sellerlane assigns a session to the UTC day of its first eligible recommendation impression and counts four nested populations:

  1. sessions with a recommendation impression;
  2. those sessions with a later recommendation click;
  3. those sessions where the same product is added to cart after the click; and
  4. those sessions where that same product is purchased by an online order linked to the session after the cart event.

The invariant is sessions ≥ clicks ≥ add-to-carts ≥ purchases. A Sellerlane visit closes at midnight UTC, so a visit token itself does not cross that boundary. If checkout completes after midnight while retaining the originating visit token, its purchase credit remains on that visit’s first recommendation- interaction day. A click after order creation cannot receive purchase credit. Bot, staff, preview, and test events are excluded. The separate Sellerlane placement/product diagnostic retains event counts and original-order attributed merchandise sales, but those fields are not shown as Shopify’s session-funnel metrics. The preset remains executable-unreconciled until a captured production Sellerlane result is compared with the corresponding captured Shopify export; the nested invariant proves Sellerlane’s behavior, not Shopify’s population or day-assignment implementation.

Customer status

  • A new customer is a customer whose qualifying order is their first eligible non-test order in that store.
  • A returning customer has at least one earlier eligible order.
  • Returning customer rate is distinct returning customers ÷ distinct customers who placed an eligible order in the period.
  • Guest orders without a stable customer identity can contribute orders and sales but cannot safely contribute to distinct-customer metrics.

Customer lifecycle reports also exclude internal POS walk-in holder records. Those rows remain valid POS sales, but they are not a stable identified customer. Gift-card-only orders do not establish customer chronology. A mixed checkout does, using only its reportable non-gift-card merchandise order.

Customer counts are order-event facts. A cancellation, restoration, return, or refund never manufactures a customer on its processed date. A customer who places both their first and a later eligible order in the same displayed period can appear once in both the first-time and returning rows; the returning-rate denominator still counts that customer once. The default New vs returning customers report is the non-time donut split. New vs returning customers over time is the day-and-customer-type area series.

Customer lifecycle sales use a different clock: original total sales use the order placement date, while signed cancellations, restorations, and refunds use their processed date. Every reversal retains the original order’s first-time or returning classification. New customer sales over time filters that ledger to first-time orders and groups by day; it does not discard later reversals. Sold gift-card face value is excluded, and monetary results fail closed rather than add or relabel unlike transaction currencies.

Customers by location selects customers by first eligible order date, then groups them by their most recent non-empty shipping location across their complete eligible order history—even if that later shipping address falls outside the selected acquisition range. It is not session IP geography or the customer’s current default address.

The Shopify-style One-time customers and Returning customers detail lists classify a customer from their complete eligible order history, even when the selected acquisition range contains only the first order. Their accepted-marketing field comes from an immutable snapshot on the most recent eligible order; today’s subscription state is never substituted. The explicit checkout checkbox is persisted before a hosted-payment redirect, and an already subscribed contact is read from the canonical consent materialization at order placement. Name and email are current customer-profile labels.

This snapshot is historical reporting evidence, not send-time permission. A campaign must still enforce the contact’s current canonical subscription and suppression state. When an order has an email address, the placement-time subscription lookup is scoped to that exact normalized address; a different email already attached to the same customer cannot lend its consent to the order address.

For these customer lists, Total amount spent is the sum of each eligible order’s event-net Total sales, floored at zero per order. It therefore includes tax, discounts, shipping, applicable fees, and processed cancellations, edits, restorations, and refunds without allowing an excess goodwill refund to create negative customer spend. Average amount spent per order divides that amount by eligible orders; it is not Shopify sales-report AOV. Native transaction currencies are never relabeled or silently added: a mixed-currency money query fails closed until historical order-time FX conversion exists.

Late identity linking can change customer classification. The resequencing worker recalculates order index deterministically so only the earliest eligible order remains the first order. First/last order chronology and cohort membership use when the order was placed in the store reporting timezone. A COD order paid days later does not move cohorts, become the customer’s latest purchase, or reorder a newer prepaid order; payment time remains a payment-reporting clock.

Live View identity mix

Live View’s visitor/session identity is browser-based: new means the retained unique token was first seen on the current store-local day, and returning means it was first seen earlier. This is independent of customer order history. The current map dots and city clusters are activity-coded and do not yet render that new/returning split; the aggregate API retains the two counts separately.

Live View’s customer mix is order-based and requires customer_id on every classified row. New/returning customer counts, order counts, attributed sales, refunds, residual refunds, cancellations, and edit reversals all use that same identity gate. Anonymous guest commerce remains financially real and therefore stays in overall Orders and Total sales, but it contributes to neither customer bucket. This prevents a guest row with is_first_order = false from being mislabelled as returning.

RFM and actual lifetime value

RFM and LTV are point-in-time customer snapshots, not daily sales totals. They include only customers with a stable customer identity and at least one eligible merchandise order placed on or before the snapshot date. Test and deleted orders are excluded. A guest order can remain in store sales, but it cannot be assigned to a customer’s RFM or LTV; a gift-card-only checkout cannot create customer purchase history.

RFM uses the customer’s complete eligible order history through the snapshot:

  • Recency is the number of store-local calendar days since the most recent eligible order was placed. Payment, fulfillment, refund, and cancellation dates do not move that order date.
  • Frequency is the number of eligible orders placed. A later refund or cancellation contributes a financial reversal but does not pretend the order was never placed.
  • Monetary value is the customer’s lifetime event-net total sales through the snapshot. Original total sales enter on order placement; signed cancellations, order edits, restorations, and successful refund allocations enter on their own processed dates. The basis includes the reportable merchandise and total-sales components in Total sales, not raw gateway refund money. Sold gift-card face value and gift-card refund value are excluded. The final per-customer value is floored at zero.

R, F, and M scores are store-relative purchaser quintiles from 1 to 5; the segment is derived from that score matrix. Customers without an eligible order remain prospects rather than receiving a fabricated purchaser score.

Actual lifetime value uses the same signed, lifetime total-sales basis and the same zero floor as RFM monetary value. It is historical customer spend in this operational report—not contribution margin, predicted future value, gateway cash, or accounting revenue. Average lifetime value is the sum of those actual customer values divided by distinct customers in the result. The LTV report’s Average order value is a separate order-time merchandise calculation—gross sales less discounts divided by eligible orders—and therefore does not divide actual LTV by orders or absorb later reversals. Predicted lifetime value and predicted-spend tier are separately labelled Sellerlane estimates, become available only after more than 100 eligible store orders, and are not Shopify’s proprietary prediction model.

Because these are snapshots, a date selects the latest state at that snapshot; adding daily snapshot rows would count the same lifetime value repeatedly. RFM, LTV, and cohort workers fail closed instead of combining an unknown or foreign order/reversal currency with the store reporting currency.

Customer cohorts

A customer enters exactly one weekly, monthly, or quarterly cohort using the store-local placement date of their first eligible merchandise order. A gift-card-only checkout cannot establish RFM/LTV purchase history or cohort membership. Payment time does not move a delayed COD order into a later cohort.

  • Period -1 / First order contains the cohort’s immutable acquisition-order count and acquisition-order net merchandise sales. Its retention display is the 100% acquisition baseline; customers returned remains zero.
  • Period 0 contains additional eligible orders in the same acquisition interval plus signed reversals processed in that interval; the first order is not counted again as a repeat order.
  • Later periods contain repeat orders placed in that period plus signed reversals processed in that period.
  • Cohort revenue means net merchandise sales. Repeat-order sales enter at order placement; line-attributed cancellations, edits, and refunds enter as signed changes on their processed date. A restoration of a prior cancellation/edit is a positive signed change. It excludes shipping, tax, duties, tips, fees, and order-grain refund residuals, and a period may therefore be negative.
  • Retention is distinct customers with a repeat order in one cohort-period cell divided by the immutable size of that exact cohort.

Financial reversals and restorations change cohort revenue only. They do not count a customer as returned; that requires an eligible repeat order placed in the cell. A period can consequently have negative revenue and zero returning customers.

Cohort date and period are required dimensions, and one exact interval is required. Omitted interval filters default to monthly; explicit interval requests must pair week/week, month/month, or quarter/quarter and use DAY or the matching granularity. The server omits totals, comparisons, and time series and rejects a result with either cell dimension removed because cohort size repeats on each period row and one customer may return in several periods; summing those rows could report retention above 100%.

Sales metrics

Analytics uses the same formulas as orders and refunds:

MetricFormula
Gross salesOriginal merchandise value before discounts
DiscountsOrder-time merchandise discount allocation
Net salesGross sales − discounts − event-dated merchandise reversals
Total salesNet sales + shipping + tax + duties + tips + additional fees ± adjustments
AOVOrder-time merchandise after discounts ÷ eligible orders
Average order quantitySUM(original merchandise quantity ordered) ÷ SUM(eligible orders) for the selected result; later reversals are excluded
Reversed quantity rateSUM(reversed merchandise quantity processed) ÷ SUM(original merchandise quantity ordered) for the selected result

Gift-card issuance is a liability, not merchandise sales. Gift-card and store-credit redemption are tenders, not discounts. Successful later refunds, returns, cancellations, and edits appear on their own processed date and can make a day or row negative.

Order-date sales include pending, unpaid/voided, and later-canceled orders. A later cancellation is a dated reversal instead of erasing the original row. Test and deleted orders are excluded; payment capture is measured separately.

Average order quantity and reversed quantity rate are aggregate ratios, not averages of daily percentages. Either is undefined when its denominator is zero. Average order quantity describes original demand and never changes when items are later reversed. Because order and reversal events use different clocks, reversed quantity rate can exceed 100% when a selected range contains reversals for orders placed earlier. That value describes event timing; it is not clamped into a more comfortable but false range.

See Financial semantics for the complete contract.

Payment metrics

Payment reports count successful capture/payment rows and successful refund rows on their transaction processed date. Transactions is the count of those two populations, Gross payments is successful captured money, Refunds is successful refunded money shown as a positive magnitude, and Net payments is gross payments minus refunds. A quiet capture period with refunds for older orders can therefore have negative net payments.

Authorizations, failures, cancellations, voids, order sales, gateway payouts, provider fees, and chargebacks are separate measures. Payment-method and gateway breakdowns use the immutable attributes snapshotted on each transaction and remain externally unreconciled until controlled provider rows are compared with a captured reference export. See Payment-flow report definitions.

An attempt identity is assigned to the reporting day of its first qualifying intent/auth/capture event. The Attempts metric therefore does not move or repeat when that lifecycle crosses midnight. A later successful capture still contributes its settled transaction and Gross payments on the capture’s own processed date; it does not create another attempt.

Fraud and chargeback rates

Risk rates and chargeback rates do not share a denominator. High-risk rate is high-risk orders divided by orders that received an authoritative risk assessment. Chargeback rate is provider disputes opened in the selected period divided by successful live gateway payments processed in that period. It does not divide by orders placed or assessed. Selected-range rates are derived from the summed numerator and denominator, so daily percentages are never added. See Chargeback-rate denominator.

Timezone and date ranges

Date presets and daily buckets use the store’s configured reporting timezone. The start is inclusive and the end is exclusive internally, which avoids double counting at midnight when adjacent periods are combined.

The event clock matters:

  • original sales and eligible order counts use order creation time;
  • refunds use successful refund processing time;
  • cancellations and edits use adjustment processing time;
  • traffic uses session/event occurrence time;
  • attribution windows end at order creation time, not later payment time.

Comparisons resolve calendar periods in the same timezone. “Previous period” is the immediately preceding equal-length calendar window; “previous year” maps to the corresponding prior-year calendar dates.

Dashboard target semantics

A dashboard target is a persisted threshold over one enabled canonical report metric. It does not copy a card’s displayed text, execute a free-form query, or create a second metric formula. Its current value is obtained from the same server-owned report executor and metric registry as the corresponding report.

Each target snapshots all of the context required to keep its meaning stable:

  • report family, metric key, metric name, and metric value type;
  • threshold direction (at least or at most);
  • typed target unit and, for money only, one ISO currency;
  • inclusive store-local start and end dates; and
  • the IANA store timezone used to resolve those civil dates.

Calendar presets resolve to their complete period when the target is created: today, Monday–Sunday week, calendar month, calendar quarter, or calendar year. They do not roll to the next period, and changing the store timezone later does not move an existing target. Custom dates are interpreted in the store timezone and then persisted with it.

Target values are non-negative and have disjoint physical representations:

UnitStored valueDisplay/input meaning
Moneysigned-capable 64-bit column, constrained non-negative for targetsInteger ISO 4217 minor units in the snapshotted store reporting currency
Count64-bit integerWhole count; never money or points-as-money
Rateinteger basis points10,000 = 100%; no implicit 0–100% cap because some business ratios can exceed 100%
Numbernumeric(30,6)General non-monetary decimal with no currency

Exactly one typed value may be present. A money target fails closed when its currency differs from the current configured reporting currency. During hydration, a current money value with another currency is returned as unavailable with a warning; the values are not summed or relabelled.

Attainment is current ÷ target × 10,000 basis points, rounded half away from zero. Thus 10,000 means 100% attainment. It is undefined for a zero target or an unavailable current value and is not capped at 100%. Achieved is a separate directional comparison: current ≥ target for at-least targets and current ≤ target for at-most targets. For an at-most target, lower attainment is therefore better.

If the canonical metric is a ratio whose denominator is zero, the current value, attainment, and achieved state remain unavailable rather than treating the ratio as zero. Point-in-time canonical metrics keep their report definition (normally the last available snapshot in the selected period); additive metrics sum their event-dated facts. The target layer never averages daily rates or invents a different aggregation.

Metric identity, unit, currency, direction, and resolved period are immutable. Only the value in the already-selected typed column can be updated. Create, update, and soft-delete events are recorded at the database boundary with actor and before/after snapshots so an administrative write cannot silently bypass the target history.

Currency

Traffic counts are currency-independent and are stored once even when a store has currency-partitioned monetary rows. Money is always scoped to an explicit currency.

Sellerlane never sums unlike currencies and labels the result as though they were one. The current launch contract is one configured store/reporting currency. Historical order-time FX conversion is not provided; if multiple order currencies are introduced, reports must keep them separated until that conversion primitive exists. Read Why historical FX exists for the distinction between report-display conversion and actual payment/payout conversion.

Location has three meanings

Always choose the location dimension that answers the business question:

LocationSourceUse it for
Session locationTrusted edge/IP geolocation captured when the visit beginsApproximate audience geography: country, region, and city
Customer locationCustomer’s saved/default address or the report’s explicitly named address sourceCustomer geography
Sales/POS locationThe order’s POS selling location, assigned inventory location, or explicitly named fulfillment locationStore/location performance and inventory operations

Session location never means the shipping address or the store that fulfilled an order. Sales location never means where the shopper’s IP was located.

At the edge, Sellerlane accepts Cloudflare country, region, city, timezone, latitude, and longitude only when the request arrived through the configured trusted proxy path. Client-supplied copies of those headers are not trusted. Coordinates are validated and retained only as Cloudflare’s approximate IP point for coarse map placement; their decimal precision is never treated as address accuracy. If named detail is missing or invalid, reports degrade to region or country instead of guessing.

The accepted edge contract is deliberately small:

  • CF-IPCountry supplies a two-letter country; Cloudflare’s XX and T1 values become Unknown rather than a fabricated country.
  • The managed visitor-location transform may add CF-Region, CF-IPCity, CF-IPContinent, CF-Region-Code, CF-Timezone, CF-IPLatitude, and CF-IPLongitude. Sellerlane trusts these detailed fields only after the production transform has been checked and the runtime trust flag enabled.
  • Latitude and longitude are accepted only as an atomic, finite, in-range pair. Named city/region detail—not the coordinate’s decimal precision—determines the reported accuracy label.
  • CF-Ray is validated and retained for request tracing even when geography is unavailable. It is not a visitor identifier.
  • CF-Connecting-IP is canonicalized and used only through keyed identity/rate limiting hashes; analytics does not persist the raw address. Pseudo IPv4 stays disabled so IPv6 visitors keep a stable identity.
  • Postal/metro codes, True-Client-IP, JA3/JA4, and other high-entropy headers are not collected merely because Cloudflare can expose them. Verified-bot flags and normalized bot score are accepted only when their managed transform is explicitly trusted.

Cloudflare’s visitor timezone never changes the store reporting timezone. It is a coarse session-location attribute; all report dates still use the configured store timezone.

Consent and privacy still apply. Declining analytics tracking prevents the visit from being persisted at all. When analytics tracking is allowed but trusted location is unavailable, the visit can produce an Unknown geography row; it must not be reassigned to the merchant’s location.

Attribution

The canonical acquisition model is last non-direct click within the configured lookback window, 30 days by default and capped at 365 days.

  • The window ends at order creation, so a delayed COD payment cannot admit a post-purchase visit.
  • A visit can match through the order’s visit token, browser identity, signed-in customer, or an audited browser-to-customer identity link.
  • Direct traffic receives credit only when there is no eligible non-direct touchpoint in the window.
  • Marketing attribution respects marketing-consent eligibility. Operational order reporting does not disappear merely because marketing consent was not granted.
  • The Traffic family is operational acquisition reporting. The Marketing family is a separate consent-qualified dataset backed by the canonical touchpoint and credit ledgers; it never re-labels Traffic rows as campaign attribution.
  • Single-touch models allocate 100% once. Multi-touch models must have credit basis points that sum to exactly 10,000 for an order.
  • Attributed order counts are order-equivalent credit. They are whole numbers for single-touch models, can be fractional inside Linear breakdowns, and are intentionally repeated in Any click participation rows.
  • Attributed sales use the order’s snapshotted currency and sales basis. Later cancellation, edit, refund, and restoration events retain the original touchpoint and model allocation and change attributed total sales on the event’s processed reporting date. They never rewrite the order-date value. Sold gift-card face value and its refund/reversal are excluded, and the gateway’s undifferentiated refunded amount is never substituted for the allocated reportable sales components.
  • Attribution-report average order value uses order-time gross merchandise sales less discounts, divided by attributed orders. It excludes shipping, tax, duties, tips, fees, and later reversals. Attributed total sales per order is a different calculation and is never presented under the AOV name.

First click, last click, linear, and any-click are separate lenses, not numbers that should be added together. Analytics Marketing defaults to Last non-direct click when no model is selected. Selecting the attribution-model dimension can compare lenses row by row; cross-model totals, comparisons, and time series are suppressed because they would multiply sessions and add incompatible answers. See Marketing attribution.

Shopify has published different wording for older marketing surfaces, including an interaction-order-value field that included shipping and tax. The 187-report manifest follows the current Analytics fields definition above; attribution model results remain NEEDS_ATTRIBUTION_RECONCILIATION until captured exports prove field-by-field agreement.

Online and POS

The channel dimension is part of every sale fact. Online and POS share the financial formulas, refund timing, currency rules, bundle allocation, loyalty, and inventory invariants, but their operational dimensions differ:

  • online reports may use session, landing page, referrer, device, and UTM data;
  • POS reports may use selling location, terminal, register session, cashier, sales staff, and payment method;
  • a POS order does not receive a synthetic online session merely to fill a traffic report;
  • a gift-card-only POS checkout belongs to gift-card liability/payment reporting, not ordinary POS order count, merchandise sales, or AOV; a mixed checkout reports only its non-gift-card merchandise as sales;
  • an online order fulfilled from a location remains online channel sales unless the report explicitly groups by fulfillment/inventory location.

Dedicated commerce report families—sales, orders, profit, vendor, finance, tips, cancellations, fulfillment, fraud, sales by location, and customer lifecycle—default to Online + POS when no channel scope is supplied. Traffic, marketing attribution, behavior, checkout, carts, search, recommendations, device, landing-page, session-location, conversion-location, and Web Vitals reports remain online-store populations. An explicit channel scope always wins; the server does not accept a channel filter and then execute an incompatible hidden default.

The executable POS product, variant, vendor, and product-type presets currently show event-dated, product-attributed Net sales, not Shopify Total sales. Shipping, tax, duties, tips, fees, and order-level refund residuals cannot be allocated to a product without inventing data. Product type is also the immutable Sellerlane sale-time kind, not Shopify’s merchant-managed product type taxonomy. These presets remain explicitly partial until those historical fields exist and captured exports reconcile them.

Exact-grain report sources

Some Shopify reports cannot be derived truthfully from a daily aggregate. These presets execute against dedicated entity/event sources:

  • Visitors right now counts distinct eligible unique_token identities whose retained visit was active in the rolling five minutes ending at query time. Multiple active visits for one identity count once; bot, staff, preview, and test traffic is excluded.

  • Payments by order emits one successful tender event per row. External and manual captures, gift-card redemptions, and store-credit redemptions are positive; refund/outflow entries are negative. Authorizations, failures, voids, and test money are excluded. Gateway/manual rows use their immutable processed time; gift-card and store-credit rows use their immutable ledger posting time.

  • Net sales from gift cards cohorts each sold gift-card line by its original order-line identity. Gross face value and discount enter on the order day; cancellation/edit/refund reversals reduce that original-sale cohort when they are processed, while the report’s Date and Sale ID remain the original sale. Re-running the original sale period can therefore show the later reversal. Gift-card redemption is tender, not another gift-card sale.

  • Shipping by order is original shipping charge less shipping discount and signed shipping reversals. Original charge uses order time; cancellation, edit, and refund reductions use processed time. Shipping tax stays in tax.

  • Items bought together counts one complete distinct current-product set per eligible order. An A + B + C basket is not expanded into three pairs, and duplicate quantities do not multiply the combination. Labels come from the current product and variant records, not sale-time title snapshots; deleting a referenced product or variant removes that purchased entity. Because the metric is an order count rather than money, it spans every order currency and does not apply the reporting-currency filter. Post-return/order-edit behavior remains captured-reconciliation work.

  • Sales by customer name groups durable customers by customer identity, so a profile rename does not split history. The current profile supplies the label; immutable order snapshots fill blank profile fields and label guest orders, but do not resurrect a deleted known customer. Original sales use order time and signed reversals use processed time.

  • Total sales by order is a signed order/reversal-event ledger. Original reportable sales use order-created time; cancellation, edit, restoration, and successful-refund rows use processed time and retain their original order identity and transaction currency. Refund-line amounts and the order-level residual are disjoint, so a refund is never deducted twice. Amount-only custom item refunds remain truthful order-grain sales reversals without an invented product allocation. Duties and return-fee columns stay unavailable until an immutable source exists for them. This is executable order/event-total coverage, not yet full Shopify finance-row parity: product-title-at-sale rows and separate shipping sale rows are also outstanding.

  • Weekly sales patterns uses the same signed sales events but groups Net sales by the event timestamp’s store-local day of week × hour of day. It is a two-dimensional heatmap over the selected event-date range, not a daily series renamed as a weekly pattern. Monday through Sunday use ISO weekday order and hours use 00:00 through 23:00.

  • The four observed Shopify bundle reports execute from one immutable order-line-component event source: Bundle component and product comparison (the captured catalog uses the older bundle_item_versus_non_bundle_sales slug), Bundle total sales over time, Total sales by bundle, and Total sales by bundle component. Original component merchandise and item tax use the order day. Successful refunds, cancellations, restorations, and order edits keep the originating component/bundle identity and use their processed day with a signed contribution. Stable source IDs and the order-time bundle title, handle, configuration hash, component title, variant, and SKU remain separate dimensions; current catalog edits never relabel historical rows.

    These presets are explicitly partial sales-component parity. Their Attributable bundle/component sales metric is signed net component merchandise plus directly retained item tax. It excludes shipping, shipping tax, duties, tips, order-only fees, and residual adjustments because those amounts have no immutable component owner. Net bundle component items is signed component quantity; it is not presented as whole parent-bundle units when a buyer reverses only part of a bundle. Sellerlane will not apportion an order-level amount or manufacture a whole-bundle reversal merely to fill a Shopify-shaped Total sales/Net items column. All four remain externally unreconciled until paired captured exports establish Shopify’s exact allocation, title, and partial-reversal behavior. The observed 184-report catalog contains four—not five—bundle slugs, so no fifth report name is invented.

Sales by discount codes remains unavailable. Combined discounts currently retain only a combined reversal amount, not an immutable allocation back to each discount application, and the sale-time percentage/fixed/free-shipping/ buy-X-get-Y/app taxonomy is incomplete. Sellerlane refuses proportional allocation or label guessing because either would fabricate financial results.

Inventory metrics

Inventory is location and time sensitive:

  • available is saleable quantity at the selected location and instant;
  • starting inventory is each inventory item/location’s first available snapshot in the selected period; an item introduced during the period begins on its introduction day;
  • ending inventory is each inventory item/location’s last available snapshot in the selected period;
  • sales-report net quantity is ordered quantity less signed, processed merchandise reversals;
  • inventory units sold is the gross flow of tracked units ordered. Refunds, cancellations, manual inventory adjustments, and transfer receipts do not reduce it;
  • percent of inventory sold is gross inventory units sold ÷ starting inventory and is undefined when starting inventory is zero;
  • sell-through is gross inventory units sold ÷ (gross inventory units sold + max(ending inventory, 0)) and is undefined when that denominator is zero. It is exposed only at variant grain: clamping after variants have already been summed would produce a different answer, so cross-variant totals and time series are withheld;
  • oversold/backordered quantity can be negative only for inventory policies that explicitly permit selling past zero. Raw ending inventory remains negative; only the sell-through denominator treats a negative ending balance as zero.

Inventory movement date and sales event date answer different questions. A return receiving can restock on a later date than the refund.

Freshness and durable ingestion

Storefront events enter a durable, idempotent outbox before background delivery. Every event has a stable identity; retrying delivery is expected and consumers must produce one business effect.

Visit resolution uses the same acceptance boundary. Session-state mutation and resolve-intent append are atomic, and the storefront does not wait for the seller network call. “Tracking available” is not claimed during a Valkey outage: without a confirmed durable mutation no visit cookie/context is exposed.

“Accepted” means the event intent is durably stored; it does not mean the event already appears in a report. Events for one browser/store identity are a strict FIFO: only that partition’s head is eligible for delivery, a retry keeps the same head, and acknowledgement promotes exactly one successor. Different identity partitions remain independently claimable, so one delayed visitor cannot hide or reorder another visitor’s due events.

Every claimed payload is structurally validated before delivery. An invalid JSON or invalid task envelope is a poison record, not a business event and not a successful delivery. The same Lua claim atomically copies its original bytes, identity, partition, reason, and quarantine time to the durable quarantine; removes only that record from the active queue; installs an idempotency tombstone; and promotes exactly the next record in that identity FIFO. A lost Redis response can therefore cause the claim to be retried, but it cannot restore the poison record, duplicate it, or discard a valid successor.

Quarantine records have no automatic expiry or retry. /health reports analyticsDispatch.quarantineDepth and oldestQuarantinedAgeMs, and any non-zero quarantine depth degrades analytics health until an operator preserves and investigates the evidence. Active queue depth excludes quarantined records. A quarantined intent is absent from financial and behavioral reports unless it is corrected and replayed as a separately auditable valid event; quarantine itself never counts as a sale, session, conversion, or adjustment.

The production invariant lives in Redis/Valkey Lua, not only in the in-memory test model. Before launch, run the outbox contract against a disposable instance of the deployed Redis/Valkey version and topology:

Terminal window
cd backend/buyer
ANALYTICS_OUTBOX_TEST_REDIS_URL=redis://127.0.0.1:6379/0 \
ANALYTICS_OUTBOX_TEST_DESTRUCTIVE_OK=true \
pnpm test:analytics-dispatch

The test deletes the analytics-dispatch-v3 namespace, so the URL must never point to a shared or production database.

Production uses either managed synchronous multi-AZ durability or a dedicated self-managed Redis with TLS, noeviction, AOF enabled, a healthy AOF writer, and appendfsync always. appendfsync everysec is deliberately rejected: it can acknowledge a short write window before disk persistence and therefore does not satisfy the outbox acceptance contract.

Reports are built asynchronously from facts and rollups. The Health view exposes queue depth/age, source watermark, last successful rollup, and freshness. A freshness state describes data delay; it never changes the metric definition.

Late-arriving events mark their actual reporting day dirty so that an old refund or identity link can be rebuilt even when it falls outside the normal rolling refresh window.

Reconciliation status

Sellerlane distinguishes three levels of support:

  1. Defined — the report has a documented semantic contract.
  2. Executable — the server can run that contract against controlled fixtures and rejects unsupported fields.
  3. Captured-reconciled — the same controlled commerce scenario has both a captured Shopify export and a captured production Sellerlane report export, with verified provenance, and passes the comparator within an explicit tolerance or approved expected-difference rule.

“Executable” does not mean “externally reconciled.” Reports that rely on proprietary third-party classifications or unavailable primitives remain clearly unavailable or approximate; they are never marked exact merely because the UI can render a chart.

Current Shopify report manifest

Advertised
187
Observed
184
Concrete
184
Unresolved
3
Enabled
164
Disabled
20
Captured-reconciled
0

Captured-reconciled reports: none. Controlled internal fixtures count as captured evidence: no.

Support-status counts
StatusCatalog entries
Executable Unreconciled101
Needs Attribution Reconciliation15
Partial Sales Components11
Partial Gateway Scope6
Partial Reversal Types6
Requires Subscriptions5
Needs Funnel Reconciliation4
Requires Shop Channel4
Partial Taxonomy3
Unresolved Catalog Entry3
Needs Close Reconciliation2
Not Applicable Shopify Protect2
Partial Acceptance Denominator2
Partial Return Components2
Partial Row Fields2
Partial Shop Taxonomy2
Requires Agentic Commerce2
Requires Risk Provider2
Requires Shop Pay2
Missing Fields Or Preset1
Partial Finance Components1
Partial Low Engagement Threshold1
Partial Semantics1
Partial Sku Identity1
Partial Tracking Presence1
Proprietary Approximation1
Requires Autopilot Marketing1
Requires Historical Fx1
Requires Managed Markets1
Requires Markets1
Disabled-reason counts
ReasonReports
Requires Subscriptions5
Requires Shop Channel4
Not Applicable Shopify Protect2
Requires Agentic Commerce2
Requires Shop Pay2
Missing Fields Or Preset1
Requires Autopilot Marketing1
Requires Historical Fx1
Requires Managed Markets1
Requires Markets1
Disabled reports (20)
ReportCategoryStatusReason
shop_campaign_roas
Shop Campaign ROAS
Behavior Requires Shop Channel This report requires Shopify Shop-channel impression, campaign, order, and attribution primitives. Market does not relabel online-store traffic or orders as Shop activity.
managed_markets_taxes
Managed Markets taxes
Finances Requires Managed Markets This report requires Managed Markets tax, duty, filing, and fee primitives. Market's ordinary tax ledger must not be presented as Shopify Managed Markets activity.
shop_pay_payments
Shop Pay payments
Finances Requires Shop Pay This report requires Shop Pay's immutable checkout/payment classification. A generic gateway or wallet label is not a defensible Shop Pay substitute.
shop_pay_transactions
Shop Pay transactions
Finances Requires Shop Pay This report requires Shop Pay's immutable checkout/payment classification. A generic gateway or wallet label is not a defensible Shop Pay substitute.
shop_channel_orders
Shop channel orders
Finances Requires Shop Channel This report requires Shopify Shop-channel impression, campaign, order, and attribution primitives. Market does not relabel online-store traffic or orders as Shop activity.
shop_payments_by_type
Shop payments by type
Finances Requires Shop Channel This report requires Shopify Shop-channel impression, campaign, order, and attribution primitives. Market does not relabel online-store traffic or orders as Shop activity.
orders_covered_by_shopify_protect
Orders covered by Shopify Protect
Fraud Not Applicable Shopify Protect Shopify Protect is a Shopify product primitive and is not represented as a Market metric.
orders_protected_by_shopify_protect
Orders protected by Shopify Protect
Fraud Not Applicable Shopify Protect Shopify Protect is a Shopify product primitive and is not represented as a Market metric.
performance_by_autopilot_marketing_activities
Performance by Autopilot Marketing Activities
Marketing Requires Autopilot Marketing This report requires Shopify Autopilot marketing-activity identity and spend/performance facts. Market has no equivalent source and does not infer it from campaigns with similar names.
shop_channel_product_impressions
Shop channel product impressions
Marketing Requires Shop Channel This report requires Shopify Shop-channel impression, campaign, order, and attribution primitives. Market does not relabel online-store traffic or orders as Shop activity.
active_subscriptions_over_time
Active subscriptions over time
Sales Requires Subscriptions This report requires a subscription contract, recurring-order lineage, interval, activation, cancellation, and one-time-versus-subscription sale snapshots. Ordinary repeat orders are not subscriptions.
agentic_total_sales_by_channel
Agentic total sales by referring channel
Sales Requires Agentic Commerce This report requires an authoritative agentic-commerce referring channel and order classification. Market does not guess agentic sales from user agents, referrers, or free-form source labels.
agentic_total_sales_over_time
Agentic total sales over time
Sales Requires Agentic Commerce This report requires an authoritative agentic-commerce referring channel and order classification. Market does not guess agentic sales from user agents, referrers, or free-form source labels.
average_profit_margin_by_market
Average profit margin by market
Sales Requires Markets This report requires an immutable sale-time market identity and market-specific pricing/currency semantics. Shipping country or transaction currency is not substituted for a configured Market.
canceled_subscriptions_over_time
Canceled subscriptions over time
Sales Requires Subscriptions This report requires a subscription contract, recurring-order lineage, interval, activation, cancellation, and one-time-versus-subscription sale snapshots. Ordinary repeat orders are not subscriptions.
new_subscriptions_over_time
New subscriptions over time
Sales Requires Subscriptions This report requires a subscription contract, recurring-order lineage, interval, activation, cancellation, and one-time-versus-subscription sale snapshots. Ordinary repeat orders are not subscriptions.
sales_by_discount_codes
Sales by discount codes
Sales Missing Fields Or Preset Unavailable for strict parity: combinable discounts can affect the same line, but current refund and order-adjustment facts retain only a combined discount reversal, not an immutable per-reversal/per-discount-application allocation. Market also lacks a canonical sale-time taxonomy that distinguishes percentage, fixed, free-shipping, buy-X-get-Y, and app discounts. Proportional allocation or taxonomy guessing would fabricate results.
subscription_vs_one_time_sales
Subscription vs one-time sales
Sales Requires Subscriptions This report requires a subscription contract, recurring-order lineage, interval, activation, cancellation, and one-time-versus-subscription sale snapshots. Ordinary repeat orders are not subscriptions.
subscriptions_sales_over_time
Subscriptions sales over time
Sales Requires Subscriptions This report requires a subscription contract, recurring-order lineage, interval, activation, cancellation, and one-time-versus-subscription sale snapshots. Ordinary repeat orders are not subscriptions.
total_sales_by_currency
Total sales by currency
Sales Requires Historical Fx Shopify groups rows by checkout currency but converts Total sales to the store currency. Market can expose native-currency rows, but cannot reproduce Shopify's value without historical order-time FX rates.
Unresolved advertised slots (3)
  • #185: Shopify paginator advertised 187 reports, but the signed-in catalog rendered only 184; slug and name intentionally not fabricated.
  • #186: Shopify paginator advertised 187 reports, but the signed-in catalog rendered only 184; slug and name intentionally not fabricated.
  • #187: Shopify paginator advertised 187 reports, but the signed-in catalog rendered only 184; slug and name intentionally not fabricated.
Catalog captured 2026-07-13T16:45:00+05:30; generated summary schema v2.

The displayed counts are not maintained in this page. The server derives them from the 187-slot manifest, writes marketing/src/data/shopify-analytics-parity-summary.json, and CI verifies the snapshot byte-for-byte with:

Terminal window
cd backend/seller
go run ./cmd/analytics-parity-summary \
-check ../../marketing/src/data/shopify-analytics-parity-summary.json

To refresh the snapshot after an intentional manifest change, replace -check with -write. The generated capturedReconciled count includes only passing report cases backed by both captured Shopify output and a captured production Sellerlane report output from the same scenario. A captured Shopify export compared with a controlled Sellerlane generator is useful field/filter evidence, but it is not production numerical parity evidence. Controlled Sellerlane fixtures prove internal financial contracts but never increase that count.

Troubleshooting a mismatch

Before comparing two numbers, align all of these:

  • store and channel scope;
  • date range, timezone, and event date;
  • currency;
  • test/deleted/bot/staff/preview exclusions;
  • order, payment, fulfillment, and refund status;
  • metric formula and denominator;
  • location type;
  • attribution model and lookback window;
  • data freshness watermark.

Then compare fact rows before rollups. A rollup can be rebuilt; an immutable financial or identity fact should be corrected only through its audited source event.

Analytics and reports

Use dashboards, reports, cohorts, exports, and Live View in Analytics and reports.