Docs

Preparing a cost file (COGS)

What a landed unit cost should contain, where to get it per platform, how effective dating really behaves, and which findings change once costs land.

What changes when costs land

The costs file is the smallest file AXIOTRA reads — two required columns — and it changes more findings than any other. Without it the detectors still run, but three of them report the wrong kind of answer: one goes quiet, one says UNKNOWN, and one overstates. Uploading a cost per SKU is usually the highest-value ten minutes in a new workspace after the orders and inventory files.

Refunds. A refund where the return arrived and the stock was never restored is reported as the refund amount plus the stranded cost of goods: potential_exposure = refund.amount + cogs × qty. With no cost row for the SKU the second term is UNKNOWN and the finding says so rather than guessing, which is correct but leaves the list sorted by refund size instead of by what is actually stranded. Note that the quantity used is the quantity on the order's first line, not the refunded quantity, so a partial refund on a multi-line order is priced generously.

Negative margin. The margin detector reads real Amazon orders and computes contribution = sale − discount − cogs − referral fee − payment fee − shipping − fulfillment for the order's first line, raising a finding only when the result is below zero. With no cost row the cost term is zero, so contribution is almost always positive and a SKU that loses money on every order is never raised. This detector does not report UNKNOWN; it simply finds nothing, which is the failure mode to watch for.

Inventory gaps. When a storefront's available quantity disagrees with the expected figure, the unit gap is priced with a modelled contribution: sale is the cost file's list_price (or the SKU's first order-line unit price), less the cost, less a channel fee of 15 percent on Amazon or 2.9 percent on Shopify, less shipping of 9.00 or 6.50 and fulfillment of 4.00 or 3.20. A missing cost row enters that model as 0.00, so the exposure on an inventory finding is the full modelled contribution and reads higher than it should. If the modelled contribution is negative the finding reports UNKNOWN instead.

The Profit screen shows the same model as one card per SKU, using the Amazon assumptions, so it is also only as good as the cost column behind it.

What a unit cost should contain

Use landed cost per sellable unit: the supplier's unit price net of trade discounts, plus inbound freight, duty and customs brokerage apportioned per unit, plus inbound handling or inspection, plus any packaging that is part of the product. Convert each element at the rate you actually paid, not at today's rate. For a shipment of mixed SKUs, apportion freight and duty by a consistent basis — units, weight or value — and keep the same basis every time, because switching it makes period-over-period margin move for no commercial reason.

Leave out everything the contribution formula subtracts separately: outbound shipping, pick and pack, payment fees, marketplace or referral fees, and refund impact. A cost that already contains outbound shipping is counted twice and makes every margin and refund finding look worse than reality. Leave out advertising, warehouse rent, salaries and other period costs too; they are not attributable to a unit, and contribution margin is not net profit.

One number per SKU per period is enough. If you carry real batch costing, the weighted average across the batches you are currently selling is the right figure to file; the detectors compare a cost with a price and a refund, so a batch-exact cost buys accuracy the rest of the pipeline cannot use.

Where the number comes from

Shopify. Each variant carries a Cost per item field, and the product CSV export includes a Cost per item column; if your export does not contain it, read it from the variant's inventory item through the Admin API or let the AXIOTRA Shopify connection do it. The connection writes one cost row per variant on every sync, dated the day of that sync, using the variant's unit cost and its price as list_price. Variants with no SKU, or with a cost of zero or blank, are skipped entirely — which is the common reason a connected store still has SKUs priced at zero cost.

Amazon. The Amazon connection brings orders, payments, fees, refunds, shipments and FBA inventory, and no costs at all. Seller Central does not hold your cost of goods, so for an FBA-only catalog the costs file is the only way the margin detector can ever fire.

Your own systems. An ERP or inventory app usually has a landed cost field already; export SKU and cost and you are done. Without one, build the file from purchase invoices: units received per SKU per shipment, the invoice unit price, and the freight and duty documents for that shipment, apportioned as described above. A spreadsheet rebuilt once a quarter is enough for the findings to be useful, and far better than no file.

The file, column by column

Required: sku and cost. Optional: currency, effective_from, effective_to, title and list_price. A minimal file is two columns wide: sku,cost with one row per SKU. The sku must match the spelling used in the orders and inventory files exactly, because every join is an exact string match; fix spelling in the sheet, not afterwards.

cost is an amount with up to two decimals. A leading currency symbol, thousands separators and surrounding spaces are stripped, and anything else fails that row with a message naming the line and the field. currency defaults to the workspace's reporting currency, and nothing is converted anywhere in the pipeline: a cost filed in EUR against a price recorded in USD is subtracted digit for digit. Keep one currency across the file and the workspace.

effective_from is a date, defaulting to 2000-01-01 when blank, and it decides which row wins — but not in the way the name suggests. The detectors take the row with the latest effective_from for every order, whatever the order's own date, so cost history is stored and not applied: last season's orders are valued at this season's cost. Two consequences follow. Do not pre-load a future price increase, because it becomes the cost of all history the moment it is uploaded. And do not expect a historical row to fix an old order's margin while a newer row exists.

effective_to is accepted, stored and never read. Closing a cost row does not stop it being used; only a row with a later effective_from replaces it.

list_price, when present, is the sale price used to price inventory gaps and the Profit screen card. Without it, the unit price from the SKU's first order line is used, which is the discounted price the first customer paid rather than your list price — worth filling in for any SKU that is usually discounted.

Uploading it, and checking the result

Upload through the Costs (COGS) card on the Import screen. A row is replaced when the same sku and effective_from arrive again, so re-uploading a corrected file is safe and is the normal weekly or monthly routine; findings recompute after each file and the stored list is refreshed. Files up to 8 MB are accepted, the first 50 row errors are reported with line numbers, and a viewer cannot import.

Settings has a second COGS box, and it behaves differently: it adds rows instead of replacing them, so the same file uploaded twice leaves duplicates, it does not refresh the stored findings — the API and the board stay stale until the next import or sync — it splits on commas with no support for quoted fields, and it assumes USD when the file has no currency column. Use the Import screen card instead.

Check three places after the upload. The Profit screen should show a card per SKU with the cost subtracted and a contribution that is no longer the full sale price. Refund findings for SKUs you just covered should show a stranded cost figure where they previously said UNKNOWN. Inventory findings should change amount, usually downwards, because the unit gap is now priced with a real cost instead of zero.

Then keep it current on one trigger: any change in supplier price, freight or duty that is material to a SKU. If you cannot keep every SKU current, keep the SKUs that carry volume and margin risk current and accept that the rest are valued approximately — but remember that a SKU with no row at all is treated as zero cost by the margin and inventory math, and as UNKNOWN by refunds, which is the difference between an approximate answer and no answer.