River
Y CombinatorBacked by Y Combinator
FREE TEMPLATE

Product Catalog Spreadsheet Template

One master row per product and one mapping row per channel listing, so bundles, duplicates and GUID-keyed menu items each have somewhere to live.

Free download  ·  No account needed

SKU Mapping

[Company] — [n] products, [n] channels, [n] listings, built [date]

One row per channel listing rather than one row per product, because a product with two listings on one channel has nowhere to go in a product row, and neither does a bundle.

ChannelChannel identifierChannel’s own name Master SKURelationshipHow it was matchedDecision

Relationship is a closed list, and every one of the five behaves differently

One to oneThe ordinary case. One listing, one product
One to one, quantity nA two-pack of a single product. Drop the quantity and the velocity reads half
One to manyA bundle or kit. Its units belong to the components, and until they are exploded every component reads slow
DuplicateTwo listings, one product, one channel. Merge it, or keep it with a reason
Channel onlyCorrectly has no master row. A drink on a till. Mark it once and it stops being an exception

How it was matched is the other column nobody keeps: exact, normalised, or manual. An exact match can be rebuilt by a script any time. A manual match exists only because somebody wrote it down, and if they did not, it gets made again from nothing next quarter, differently.

Every free product catalogue template is one sheet with one identifier column, which assumes every system agrees on what the identifier is. The platforms say otherwise in their own documentation. Shopify's variant SKU is an optional, case-sensitive string, Square records one only if any was ever typed in, and Toast keys menu items by a generated GUID. So this pack is a join instead: one master row per product, one mapping row per channel listing.

Hollis Row Coffee is an invented roaster with 54 products, 171 listings across those four channels and $458,300 of revenue over 90 days. Matched as literal strings, 103 listings joined. Upper-casing, stripping separators and stripping leading zeros lifted that from 60.2 to 70.2 percent, which is the figure a cleanup pass reports. It fixed 17 rows: a trailing space, an underscore, a zero a spreadsheet added. All cheap, and none of them the problem.

Fifty-one listings remained, and one match rate cannot describe them, because they break a per-product report four ways that never cancel. Fourteen are correct and only look broken. Twenty-one drop rows, understating totals by $53,000. Five split one product across two listings that both sell, so a ranking by revenue puts both halves under the cut. Eight are bundles hiding 4,748 component units, which understated measured velocity by up to 82.5 percent and moved seven reorder points by a quarter or more. Three reused an identifier across a size change.

The pack, worked through on 171 listings

The SKU Mapping, Discrepancy Register and Price Consistency Check sheets, and the bundle explosion behind them.

SKU Mapping

Hollis Row Coffee. 54 products, 171 listings across four channels, 90 days. Twenty rows of the 171, chosen to show all five relationships and all three match methods.

ChannelChannel identifierChannel’s own name Master SKURelationshipMatched
ShopifyHR-ETH-250Ethiopia Guji 250g HR-ETH-250One to oneExact
Shopifyhr-eth-250␣ Ethiopia Guji 250g (Wholesale)HR-ETH-250 DuplicateNormalised
ShopifyHR_KEN_250Kenya Nyeri 250g HR-KEN-250One to oneNormalised
ShopifyHR-FILTER-0100Paper Filters 02 HR-FILTER-100One to oneNormalised
AmazonHRETH250 Hollis Row Ethiopia Guji Whole Bean Coffee 8.8ozHR-ETH-250 One to oneNormalised
AmazonCOF-4471-ETH Hollis Row Ethiopia Guji, 2-packHR-ETH-250 One to one, qty 2Manual
AmazonCOF-4482-COL Hollis Row Colombia Huila Whole BeanHR-COL-250 One to oneManual
AmazonFBA-HR-HOUSEHollis Row House Blend 12oz HR-HOUSE-250One to oneManual
AmazonHR-GIFT-TRIO Hollis Row Single Origin Gift Trio HR-ETH-250 + HR-COL-250 + HR-SUM-250 One to manyManual
AmazonHR-SAMPLER-4 Hollis Row Four Origin Sampler HR-ETH-250 + HR-COL-250 + HR-SUM-250 + HR-KEN-250 One to manyManual
SquareHR-ETH-250Ethiopia Guji 250g HR-ETH-250One to oneExact
Square(empty)Colombia Huila 250g HR-COL-250One to oneManual
Square(empty)Sumatra Lintong 250g HR-SUM-250One to oneManual
SquareSQ-CAFE-PAIRMug and Bag HR-HOUSE-250 + HR-MUG-12One to many Manual
SquareSQ-TASTING-6Six Origin Tasting Box 6 componentsOne to many Manual
SquareSQ-TOTE-01Canvas Tote Channel onlyDecided
Toasta4f1c9e2-7b33-4d18-9c60-2e51d8a7b104 Ethiopia Guji BagHR-ETH-250One to one Manual
Toastb7e2d1a8-3c44-4f92-8a15-6d90c3e5f271 House Blend BagHR-HOUSE-250One to one Manual
Toastd5a9c2e7-1f68-4b30-9d72-8c41e6b0a953 Latte, 12 ozChannel only Decided
Toaste8b3f6d1-5c79-4a24-8e91-3b60d7c2a485 Cold Brew, 16 ozChannel only Decided

The second Shopify row is the same identifier in lower case with a trailing space, which is a different string and therefore a second listing selling its own inventory. The two empty Square rows are the field being optional. Every Toast row is a GUID, so all 23 are manual, and that is the shape of the system rather than a data quality problem: a GUID is stable, so each mapping is made once and holds.

The join, before and after the cleanup everybody runs

Exact means the channel identifier equals the master SKU as a literal string. Normalised means upper-cased, non-alphanumerics stripped, leading zeros stripped inside numeric groups.

ChannelListingsExactExact rateMiss Fixed by normalisingResidual
Shopify, the reference system5851 87.9%75 2
Amazon4122 53.7%194 15
Square4930 61.2%198 11
Toast230 0.0%230 23
All171 10360.2% 6817 51
What the cleanup reportsValue
Exact join103 of 171, 60.2%
After normalising120 of 171, 70.2%
Listings it fixed17, or 9.9% of the file
Listings still needing a person 51, or 29.8% of the file
The 51, by classListingsShare of residual Revenue, 90 days
A different identifier namespace21 41.2%$53,000
Channel-only by design, and correct14 27.5%$38,600
A bundle covering several products8 15.7%$17,400
A duplicate listing inside one channel5 9.8%$11,200
An identifier reused across a change3 5.9%$1,700

The 17 listings normalising fixed were a trailing space, an underscore where a hyphen belonged and a zero a spreadsheet added. All mechanical, all cheap, all permanently fixed. The 51 it did not fix carry $121,900 of the 90 days, and 14 of them are not errors at all: nothing on this page is a bigger saving than writing down that a latte is supposed to have no product row.

Discrepancy Register

Twelve rows covering the 51, each carrying the direction it pushes a per-product report. The directions do not cancel, so no single total reveals any of them.

IDClassChannelListingsWhat it does DirectionRevenue
D-01Different namespaceAmazon 8Dropped from any per-product report Understated$44,500
D-02Different namespaceToast 8Dropped from any per-product report Understated$3,300
D-03Different namespaceSquare 5Dropped from any per-product report Understated$5,200
D-04DuplicateShopify2 One product reported as two, each below the ranking cut Split$8,400
D-05DuplicateAmazon2 Wholesale beside retail, both wantedSplit $1,900
D-06DuplicateSquare1 A counter listing and a pre-order listingSplit $900
D-07BundleAmazon4 All revenue booked to the bundle SKUMisattributed $11,600
D-08BundleSquare4 All revenue booked to the bundle SKUMisattributed $5,800
D-09Identifier reusedAmazon 1Filters went 100 count to 80 count, same SKU Both directions
D-10Identifier reusedToast 2Two bags went 250 g to 340 g, same identifier Both directions$1,700
D-11Channel-onlyToast13 Drinks and prepared food, no packaged equivalentCorrect $36,900
D-12Channel-onlySquare1 Counter-only merch, never listed elsewhereCorrect $1,700
DirectionRevenueWhich report it lands on
Understated$53,000 Any per-product total, low by the whole amount
Split$11,200 A ranking by revenue, where both halves fall below the cut
Misattributed$17,400 A ranking by margin, where the bundle carries no cost
Both directions$1,700 Every per-unit figure computed across the change date
Real reporting error $83,300 18.2% of the 90 days, on $458,300 of revenue
Correctly unmatched$38,600 None, once somebody records that it is fine

The split class hides best. Two Shopify products each picked up a second listing in a 2024 re-import and both listings kept selling, so a top ten by product revenue put both halves under the cut and the product that should have led the list never appeared on it. Total revenue is right, product count is right, ranking is wrong.

The bundles, and the price check

Eight bundle listings sold 1,660 units and contained 4,748 component units across ten products. Those units came out of the same stock as everything else and appeared in no per-SKU velocity figure.

SKUMeasured a dayHidden a dayTrue a day Understated byReorder point, measuredTrueMove
HR-DRIPPER-011.91.6 3.582.5%95 170+79%
HR-SUM-25015.49.7 25.163.3%583 947+62%
HR-MUG-123.11.9 5.059.9%154 244+58%
HR-DEC-1KG2.41.3 3.755.1%61 93+52%
HR-COL-25022.19.7 31.844.1%835 1,199+44%
HR-ETH-25034.813.7 48.539.4%1,314 1,827+39%
HR-KEN-2508.62.9 11.533.9%326 435+33%
HR-FILTER-1006.81.6 8.423.0%119 145+22%
HR-HOUSE-25041.28.2 49.419.9%1,024 1,224+20%
HR-DEC-25011.92.1 14.018.0%297 349+18%
Amazon priceReferral fee rateFeeNet proceeds
$14.998%$1.20 $13.79
$15.008%$1.20 $13.80
$15.0115% $2.25$12.76
$15.5015% $2.33$13.18
$16.0015% $2.40$13.60
$16.2415%$2.44 $13.80
SKUWasNowNet wasNet now Per unitUnits90 days
HR-ETH-250$14.50 $15.50$13.34$13.18 −$0.171,340 −$221
HR-COL-250$14.50 $15.50$13.34$13.18 −$0.17980 −$162
HR-SUM-250$14.75 $15.75$13.57$13.39 −$0.18720 −$131
HR-KEN-250$14.75 $15.75$13.57$13.39 −$0.18605 −$110
Gross revenue on Amazon +$3,645
Net proceeds −$625

Seven of the ten reorder points move by a quarter or more, and the largest movers are the best sellers, which follows: a bundle gets built around whatever already sells. The fee table is a step rather than a slope, so every price from $15.01 to $16.23 nets less than $15.00 does. A catalogue-wide dollar increase walked four SKUs into that band, and only one of the two resulting lines is on a revenue report.

What's in the pack

01

SKU Mapping sheet

One row per channel listing, not one row per product. Carries the channel's own identifier and name, the relationship, the quantity per unit, and whether the match was exact, normalised or manual.

02

Master Catalogue sheet

One row per product with declared net content, a status and an effective date, so a superseded product keeps its own row and last year still joins to the thing it was actually describing.

03

Discrepancy Register sheet

Every unjoined listing classified, with the direction it pushes a report and the revenue behind it. Understated, split, misattributed or wrong both ways, reported separately rather than summed.

04

Price Consistency Check sheet

Expected price against actual, per channel, with the fee rule in the row and net proceeds beside the price. Margin after every fee layer is a separate exercise.

05

Naming Standard

How identifiers are formed, and the rule against putting anything mutable inside one. This is how HR-HOUSE-250 came to name a 340 gram bag and stayed that way on all four channels.

06

Channel Mapping Note

Per channel: what it keys a listing by, whether the field is required, whether it can be edited after creation, and whether it can be joined by a string rule at all. Two of four here cannot.

07

Bundle and Kit Note

Components and quantities per bundle, and the measured against true velocity table. Downstream, that is what a reorder point was quietly getting wrong on the top SKU in the catalogue.

08

How the Mapping Was Built

The whole worked catalogue: both join rates, the 51 residual rows by class, the four directions priced, the bundle explosion, and the referral fee tier that made a price rise cost money.

09

Eight prompts, one per decision

Build the master, normalise and count what it missed, map each channel, explode the bundles, find the duplicates, mark the channel-only rows, check prices against the policy, and supersede a changed product.

How it works

  1. 1

    Download it or install it

    Take the four blank sheets and three notes as CSV and Word files with no account, or install the whole pack into a private Space with the agent primed on the join.

  2. 2

    Send every channel's export

    A storefront export, a marketplace listing report, a point of sale item list, a menu export. Ninety days of sales by identifier alongside, since the revenue is what makes a mismatch worth attention.

  3. 3

    Read the residual, not the rate

    You get both join rates and then the rows normalising could not reach, split into the five classes, each with a count and the revenue riding on it.

  4. 4

    Decide once, and write it down

    Each residual row gets a mapping, a merge, a channel-only flag or a superseded master row, recorded with who decided and when, so it never comes back as a finding.

Frequently asked questions

Is this template free?

Yes. Four sheets, four documents and eight prompts with no signup and no card, and they are the same files the agent works in. Editing with AI is the optional path, where your own channel exports become the mapping and the discrepancy register.

What format are the downloaded files?

CSV for the Master Catalogue, SKU Mapping, Discrepancy Register and Price Consistency Check, and Word for the Naming Standard and the notes, zipped together. Excel, Numbers, Sheets, Word, Pages and Google Docs all open them directly.

Why two sheets instead of one?

Because a channel's identifier is a fact about the relationship, not about the product. Put it in a product row and there is nowhere to hold a bundle, a duplicate listing, a two-pack, or a fifth channel. One mapping row per listing holds all four.

What is wrong with reporting a match rate?

It covers four errors pushing in different directions, so they never cancel and no total shows them. Dropped rows understate, duplicates split, bundles misattribute, and a reused identifier is wrong both ways at once. Report the four, or report nothing.

When does a product change need a new identifier?

When the declared net content, the formulation, the pack count or the unit of sale changes. GS1 is explicit that any change to the declared net content requires a new GTIN, and gives a bag of snacks going from 680 to 794 grams as the example.

Why does the price check need the fee rule?

Because a referral fee is often a step, not a percentage. Amazon charges 8 percent at or below $15.00 and 15 percent above it in Grocery and Gourmet, so $15.01 nets $1.04 less than $15.00 and every price up to $16.23 nets less than the boundary.

Can it just fuzzy-match the names for me?

It will offer candidate pairs for you to confirm, and it will not accept them silently. On a catalogue with sizes in it a fuzzy pass joins the 250 gram bag to the 1 kilogram bag, and a wrong join lands on a revenue report looking correct.

Find out how many of your listings join to nothing

Download the blank pack as Word and CSV files, or open it in River, send every channel's product export, and get the join rate and the rows that need a decision back first.

Edit with AI