File specifications,
column by column
The exact shape of every file you send us: the columns, the types, the values that identify a row, and what happens when something does not validate.
On this page
How to read this page
The conventions every file follows, so you only have to learn them once.
Every file is a comma-separated text file with one header row. The rules below are the same for all of them, so your export job implements them once. Column order does not matter, and columns we do not recognise are ignored and listed back to you rather than treated as an error — you can add fields to your extract without breaking anything.
| Rule | What we accept | Notes |
|---|---|---|
| Format | CSV — comma separated, double-quoted | No spreadsheets, no fixed width, no pipe or tab delimiters. |
| Encoding | UTF-8, no byte-order mark | A byte-order mark corrupts the name of your first column. |
| Header row | Row 1, lower case with underscores | Exactly the names in this page. Order is irrelevant; extra columns are ignored. |
| Line endings | Unix or Windows | Mixed extract hosts are normal; both work. |
| Dates | 2026-07-25 | Year-month-day only. Never DD/MM/YYYY, never a spreadsheet date number. |
| Timestamps | 2026-07-31T22:15:00Z | UTC, with the Z. |
| Yes / no | Y or N | true/false, 1/0 and Yes/No are also accepted, in any case. |
| Empty values | Leave the field empty | Never write NULL, NA, N/A or a dash. Those four are rejected on purpose, so a broken export is loud instead of silently zero. |
| Numbers | 12.50 and -12.50 | No thousands separators, no currency symbols, no bracketed negatives. |
| Percentages | 0.0500 means 5% | Fractions, never percents. See the warning below. |
| Leading zeros | "0990001440016" | Quote any identifier that can start with a zero, so it survives the round trip. |
| Several values in one field | Pipe separated | Used only by a handful of columns, all flagged in the tables. |
| Compression | .gz on any file | Optional below 50 MB uncompressed, required above it. |
| Money | Already converted | Every money column, in every file, is in one reporting currency. We do not convert currencies. |
Where files go, and what to call them
You write into a private folder that only your workspace can read or write — how that isolation is enforced is in Security and data protection. Each feed has its own folder, and that folder name is what tells us which feed the file is — the file name itself is a label and a cross-check, never the router. Use any unique value per delivery for the upload folder.
File names are <feed_name>_<YYYYMMDD>.csv, optionally .csv.gz, where the date is the business date the data describes rather than the date you sent it. Split parts take _p<NN>of<NN> and corrections take _r<N>; both are set out in The data we need from you.
<your-workspace>/inbox/feed=inventory_position/<upload-id>/inventory_position_20260731.csv.gz <your-workspace>/inbox/feed=sales_weekly/<upload-id>/sales_weekly_20260731.csv.gz <your-workspace>/control/2026-07-31/_manifest_1.json <- written last
The words used in every table
- Required
- We cannot use the row without it. On a daily file the row is rejected; on a master or configuration file the whole file is.
- Optional
- Leave it empty and we apply the default given in the Notes column. Empty is a real answer — do not send a placeholder.
- Conditional
- Required only in a stated case, and it must be empty otherwise. Size curves are the only feed that uses this.
- The columns that uniquely identify a row
- Listed under every feed. We match what you send against what we already hold on those columns: if the row is there we update it, if it is not we add it. Sending the same file twice is safe.
- What happens to a row that fails
- Day-to-day files (stock, sales, orders, forecast) reject only the bad row and load the rest. Master and configuration files are all-or-nothing: one bad row and nothing loads, because a half-loaded product list quietly makes every roll-up on the dashboard wrong with no error on screen.
- Rejected rows come back to you
- Each delivery produces a rejects file listing the row number, the column, the value we saw, the reason, and a suggested fix — for example that store “0115” probably meant store 115, Riverside Mall.
Load order
Products before inventory, stores before sales. Anything that arrives early waits.
Files reference each other. A sales row names a size and a store, so both must exist first. You can drop everything into one delivery and let us work out the order — the list below is what that order is, and it is the order to use for your first load.
- 1Roots — nothing depends on anything
fiscal_calendar,dc,vendor,product_style_color,store_group - 2Sizes, stores and vendor terms
product_sku,store,vendor_style_terms - 3Mappings, curves and date windows
store_dc_mapping,product_dc_mapping,product_store_eligibility,size_curve,store_status_period,product_status_period,product_supersession,new_store,constraint_set,product_store_rule - 4Transactions
inventory_position,purchase_order,sales_weekly,price_promo - 5Forecast
forecast_granular, thenforecast_layerif you send one - 6The completion file (manifest), lastIt says the delivery is finished. Write it only once every file above has finished uploading. Section 07 is entirely about it.
| Feed | How often | Needs loaded first |
|---|---|---|
fiscal_calendar | Once a year, then on change | — |
dc | On change | — |
vendor | On change | — |
product_style_color | Daily | — |
store_group | On change | — |
product_sku | Daily | product_style_color |
store | Weekly, or on change | store_group, if you use group names |
vendor_style_terms | Monthly, or on change | vendor, product_style_color |
store_dc_mapping | Weekly, or on change | store, dc |
product_dc_mapping | Weekly, or on change | product_sku, dc |
product_store_eligibility | Weekly, or on change | product_style_color, store |
size_curve | On change | product_style_color, product_sku, store |
store_status_period | On change | store |
product_status_period | Weekly | product_style_color |
product_supersession | On change | product_style_color |
new_store | On change | store |
constraint_set | On change | — |
product_store_rule | On change | — |
inventory_position | Daily | product_sku, store, dc |
purchase_order | Daily | product_sku, dc, vendor |
sales_weekly | Daily | fiscal_calendar, product_sku, store |
price_promo | Weekly | fiscal_calendar, product_style_color |
forecast_granular | Weekly | fiscal_calendar, product_sku, store |
forecast_layer | Weekly | fiscal_calendar, product_style_color |
Master files
Your planning universe: the calendar, the locations, the range and the sizes.
You send these once at onboarding, then again whenever they change. Most teams find it easier to send a complete refresh nightly or weekly than to work out what changed; that is fine and is what we expect. A complete refresh never deletes anything — a product or store that disappears from the file is retired, not erased, so two years of history survives a bad extract.
fiscal_calendar — your retail week grid
Which calendar dates make up week 17 of your 2026. Every weekly grid in the platform is addressed by week_start_date, so this file is the root of the whole load.
File name: fiscal_calendar_20260731.csv
| Column | Type | Required | Example | Notes |
|---|---|---|---|---|
week_start_date | date | required | 2026-07-25 | The first day of the retail week. Must fall on your week-start weekday. |
week_end_date | date | required | 2026-07-31 | Must be week_start_date plus six days. We check it rather than assume it. |
fiscal_year | whole number | required | 2026 | Four digits. |
fiscal_week_no | whole number | required | 17 | 1 to 53, unique within the fiscal year. |
week_label | text 16 | optional | 07/25-07/31 | The heading shown above the week on every grid. |
month_label | text 16 | optional | July_2026 | How weeks roll up into months. |
Uniquely identifies a row: week_start_date.
week_start_date,week_end_date,fiscal_year,fiscal_week_no,week_label,month_label 2026-07-18,2026-07-24,2026,16,07/18-07/24,July_2026 2026-07-25,2026-07-31,2026,17,07/25-07/31,July_2026 2026-08-01,2026-08-07,2026,18,08/01-08/07,August_2026
dc — your distribution centres
File name: dc_20260731.csv
| Column | Type | Required | Example | Notes |
|---|---|---|---|---|
dc_code | text 32 | required | DC-CENT | No spaces. This is also the location_code you send on DC stock rows. |
dc_name | text 128 | optional | Central DC | |
city | text 64 | optional | Columbus | |
country | text 64 | optional | US | Two-letter country code preferred. |
serves_channels | pipe list | optional | B&M|Ecom | Any of B&M and Ecom. Defaults to both. |
status | text 16 | optional | Active | Active or Inactive. Defaults to Active. |
capacity_units | whole number | optional | 950000 | Single units, not cases. |
Uniquely identifies a row: dc_code.
dc_code,dc_name,city,country,serves_channels,status,capacity_units DC-EAST,East Coast DC,Newark,US,B&M|Ecom,Active,820000 DC-CENT,Central DC,Columbus,US,B&M|Ecom,Active,950000 DC-WEST,West Coast DC,Ontario,US,B&M|Ecom,Active,760000
store — every selling location
Including e-commerce, which you send as a store with channel set to Ecom. Nothing in the platform works without stores.
File name: store_20260731.csv
| Column | Type | Required | Example | Notes |
|---|---|---|---|---|
store_code | text 32 | required | 115 | Quote it if it can carry leading zeros. |
store_name | text 128 | optional | Riverside Mall | Shown on every store screen and in the rejects file. |
channel | text | required | B&M | Exactly B&M or Ecom. |
region | text 64 | optional | Midwest | A filter everywhere, and usable in assortment rules. |
state | text 64 | optional | MI | Same. |
store_tier | text 32 | optional | A | Your own grading scale. Filter and rule scope. |
store_group_default | text 64 | optional | Flagship | Should match a group you send in store_group. An unknown name is a warning, not a rejection. |
open_date | date | optional | 2011-08-19 | |
close_date | date | optional | — | Leave empty unless status is Closed. |
capacity_units | whole number | optional | 240 | Units the store can physically hold. Caps what allocation sends it. |
status | text 16 | optional | Open | Open, Closed or Pending. Defaults to Open. Temporary closures belong in store_status_period. |
Uniquely identifies a row: store_code.
store_code,store_name,channel,region,state,store_tier,store_group_default,open_date,close_date,capacity_units,status 115,Riverside Mall,B&M,Midwest,MI,A,Flagship,2011-08-19,,240,Open 704,Northgate Center,B&M,East,VA,A,Flagship,2009-11-12,,250,Open ECOM01,Ecom - National,Ecom,National,,Online,Ecom,2015-01-01,,,Open
vendor — the suppliers you raise orders on
File name: vendor_20260731.csv
| Column | Type | Required | Example | Notes |
|---|---|---|---|---|
vendor_id | text 64 | required | V-100 | |
name | text 128 | required | Apex Footwear Co. | Shown on every purchase order. |
incoterm | text 16 | optional | FOB | FOB, DDP, EXW, CIF or DAP. |
currency | text 8 | optional | USD | Three-letter code. |
status | text 16 | optional | Active | Active or Inactive. Defaults to Active. |
Uniquely identifies a row: vendor_id.
vendor_id,name,incoterm,currency,status V-100,Apex Footwear Co.,FOB,USD,Active V-200,Northwind Athletic,DDP,USD,Active V-300,Corso Leather Works,FOB,USD,Active
product_style_color — every style-colour you plan
The thing a buyer orders and an allocator allocates: 994257 “Trailblazer Runner White/Frost”, not the style behind it and not the individual size.
File name: product_style_color_20260731.csv
| Column | Type | Required | Example | Notes |
|---|---|---|---|---|
style_color_id | text 64 | required | 994257 | Quote it to preserve leading zeros. |
description | text 255 | optional | Trailblazer Runner White/Frost | Shown on every grid. Worth sending. |
style_id | text 64 | optional | 9942 | The parent style. |
color | text 64 | optional | White/Frost | Usable as a rule scope. |
division | text 64 | optional | Footwear | See the note below — optional, but stocking rules scope on it. |
department | text 64 | optional | Men's Footwear | Same. |
class | text 64 | optional | Running | The header is class, not class_name. |
subclass | text 64 | optional | Road Running | |
product_type | text 32 | optional | Core | Your own vocabulary — Core, Seasonal, Fashion. |
lifecycle_status | text 32 | optional | Reg Mid Life | Your own vocabulary — New, Reg Mid Life, Clearance. |
product_group | text 64 | optional | Performance Running | |
launch_date | date | optional | 2025-10-18 | Drives the New Products alert for the first four weeks. |
image_url | text 1024 | optional | https://cdn.example.com/img/994257.jpg | Must start https. Plain http is rejected because it breaks the page it is shown on. |
unit_cost | decimal | optional | 54.00 | Falls back to the cost on the purchase order line when empty. |
unit_retail | decimal | optional | 120.00 | Used for value KPIs when net_sales is absent. |
active_flag | Y/N | optional | Y | Defaults to Y. N means retired and excluded from planning. |
Uniquely identifies a row: style_color_id.
style_color_id,description,style_id,color,division,department,class,subclass,product_type,lifecycle_status,product_group,launch_date,image_url,unit_cost,unit_retail,active_flag 994257,Trailblazer Runner White/Frost,9942,White/Frost,Footwear,Men's Footwear,Running,Road Running,Core,Reg Mid Life,Performance Running,2025-10-18,https://cdn.example.com/img/994257.jpg,54.00,120.00,Y 998503,Dash Runner K Blue/Volt,9985,Blue/Volt,Footwear,Kids' Footwear,Running,Kids Running,Core,Reg Mid Life,Kids Performance,2026-01-10,https://cdn.example.com/img/998503.jpg,28.00,65.00,Y
product_sku — every size of every style-colour
The thing that is physically counted. Stock, sales, orders and forecast all reference a size, so nothing transactional loads before this file does.
File name: product_sku_20260731.csv
| Column | Type | Required | Example | Notes |
|---|---|---|---|---|
sku_id | text 64 | required | 99425703 | Unique across your whole range, not just within the style. |
style_color_id | text 64 | required | 994257 | Must already exist in product_style_color. |
size | text 32 | required | 8 | As shown to the customer. Spell it identically here and in size_curve. |
size_sort_order | whole number | optional | 2 | Left-to-right order of size columns. Defaults to the order sizes first appear. |
barcode | text 64 | optional | "0990001440016" | Quote it — barcodes start with zeros. |
Uniquely identifies a row: sku_id. A size may appear only once within a style-colour; two rows claiming size 9 make the size curve unresolvable and the file is rejected.
sku_id,style_color_id,size,size_sort_order,barcode 99425701,994257,6,0,"0990001440016" 99425702,994257,7,1,"0990001440023" 99425703,994257,8,2,"0990001440030"
vendor_style_terms — how the vendor actually ships
The four numbers that turn “we need 137 pairs” into an order you can place: lead time, minimum, pack size and order multiple.
File name: vendor_style_terms_20260731.csv
| Column | Type | Required | Example | Notes |
|---|---|---|---|---|
vendor_id | text 64 | required | V-100 | Must exist in vendor. |
style_color_id | text 64 | required | 994257 | Must exist in product_style_color. |
lead_time_days | whole number | optional | 21 | Vendor to distribution centre. Defaults to 21. |
moq_eaches | whole number | optional | 48 | Minimum order in single units, not packs. Defaults to 0. |
order_multiple_packs | whole number | optional | 1 | Defaults to 1. |
pack_size_eaches | whole number | optional | 6 | Units per pack. Defaults to 1. |
order_cycle | text 16 | optional | monthly | weekly or monthly. Defaults to monthly. |
valid_from | date | optional | 2026-01-01 | |
valid_to | date | optional | — | Empty means open-ended. |
Uniquely identifies a row: vendor_id + style_color_id. One row per pair — a style sourced from two vendors gets two rows.
vendor_id,style_color_id,lead_time_days,moq_eaches,order_multiple_packs,pack_size_eaches,order_cycle,valid_from,valid_to V-100,994257,21,48,1,6,monthly,2026-01-01, V-100,994258,21,48,1,6,monthly,2026-01-01, V-100,994260,21,48,1,6,monthly,2026-01-01,
Daily files
Stock, orders and sales — the three files that decide what tonight's run recommends.
These arrive every night before the overnight run. They are large and they come from operational systems, so we reject bad rows one at a time rather than losing a whole night’s position to one size that exists in the till system but not yet in your product file.
inventory_position — where the stock is right now
How many of each size are sitting in each store and each distribution centre. This is the single most important file you send.
File name: inventory_position_20260731.csv.gz
| Column | Type | Required | Example | Notes |
|---|---|---|---|---|
sku_id | text 64 | required | 99425703 | Must exist in product_sku. |
location_type | text | required | STORE | STORE or DC, upper case. |
location_code | text 32 | required | 115 | A store_code when STORE, a dc_code when DC. Checked against both lists. |
oh | whole number | required | 0 | On hand, single units. May be negative — that is a real till condition and it loads. |
it | whole number | optional | 6 | In transit. Defaults to 0. |
oo | whole number | optional | 0 | On order. Defaults to 0. On DC rows this should agree with your open orders. |
as_of_date | date | required | 2026-07-31 | The date the position was taken. Always send it, even when nothing moved. |
Uniquely identifies a row: sku_id + location_type + location_code.
sku_id,location_type,location_code,oh,it,oo,as_of_date 99425703,STORE,115,0,6,0,2026-07-31 99425704,STORE,115,2,6,0,2026-07-31 99425703,STORE,704,4,0,0,2026-07-31 99425703,DC,DC-CENT,312,144,300,2026-07-31
purchase_order — what is on the water
Every open order on a vendor: what is coming, to which distribution centre, and when. One row per order line, with the order-level columns repeated on each line.
File name: purchase_order_20260731.csv
| Column | Type | Required | Example | Notes |
|---|---|---|---|---|
po_id | text 64 | required | 99425703DC-CENT202617 | Your order number, whatever shape it is. We keep it verbatim and never parse it. |
vendor_id | text 64 | optional | V-100 | An unknown vendor loads with a blank vendor and a warning — we would rather keep the units. |
dc_code | text 32 | required | DC-CENT | Must exist in dc. An unknown one rejects the row. |
order_placed_date | date | optional | 2026-07-06 | |
po_status | text | optional | Approved | Approved, Pending, Received or Cancelled. Defaults to Approved. |
sku_id | text 64 | required | 99425703 | The line is at size level. |
style_color_id | text 64 | optional | 994257 | Derived from the size when empty. If it disagrees with the size's real parent the row is rejected. |
po_delivery_date | date | required | 2026-08-08 | Expected receipt at the distribution centre. Drives the inbound week in the projection. |
on_order_units | whole number | required | 300 | Single units. May be negative for a cancellation. |
line_status | text | optional | Approved | Same four values. Defaults to the order status. |
cost | decimal | optional | 54.00 | Landed cost per unit. |
Uniquely identifies a row: po_id + sku_id + po_delivery_date. Whenever an order appears in a file we replace all of its lines with the ones in that file, so re-sending your whole open order book every night is safe and is what we recommend.
po_id,vendor_id,dc_code,order_placed_date,po_status,sku_id,style_color_id,po_delivery_date,on_order_units,line_status,cost 99425703DC-CENT202617,V-100,DC-CENT,2026-07-06,Approved,99425703,994257,2026-08-08,300,Approved,54.00 99425704DC-CENT202617,V-100,DC-CENT,2026-07-06,Approved,99425704,994257,2026-08-08,360,Approved,54.00 99425703DC-EAST202618,V-100,DC-EAST,2026-07-13,Approved,99425703,994257,2026-08-15,180,Approved,54.00
sales_weekly — what actually sold
Units and money, per size, per store, per retail week. At onboarding send 104 weeks if you can and never fewer than 52. After that, send the last four weeks every night — restating the recent weeks absorbs late postings, voids and returns without any risk of double counting.
File name: sales_weekly_20260731.csv.gz
| Column | Type | Required | Example | Notes |
|---|---|---|---|---|
week_start | date | required | 2026-07-25 | Must be a week_start_date in fiscal_calendar. |
sku_id | text 64 | required | 99425703 | Must exist in product_sku. |
store_code | text 32 | required | 115 | Must exist in store. |
units | decimal | required | 6 | Net of returns. May be negative — a returns-only week is a legitimate row. |
net_sales | decimal | optional | 684.00 | After discount, excluding tax. |
cogs | decimal | optional | 324.00 | |
margin | decimal | optional | 360.00 | Defaults to net_sales minus cogs. Send it only if your margin definition differs. |
discount_pct | decimal | optional | 0.0500 | A fraction. 0.0500 is 5%. |
Uniquely identifies a row: week_start + sku_id + store_code.
week_start,sku_id,store_code,units,net_sales,cogs,margin,discount_pct 2026-07-18,99425703,115,7,798.00,378.00,420.00,0.0500 2026-07-25,99425703,115,6,684.00,324.00,360.00,0.0500 2026-07-25,99425703,704,5,600.00,270.00,330.00,0.0000
Weekly files
Your demand forecast, and the prices you plan to sell at.
forecast_granular — your demand forecast
Your forecasting engine’s answer: how many of each size each store will sell each week. One file is one version of the forecast; the newest one becomes the version every screen uses, and we keep the previous ones so accuracy can be measured against them.
File name: forecast_granular_20260731.csv.gz
| Column | Type | Required | Example | Notes |
|---|---|---|---|---|
week_start | date | required | 2026-08-01 | Must be a week in fiscal_calendar. |
sku_id | text 64 | required | 99425703 | Must exist in product_sku. |
store_code | text 32 | required | 115 | Must exist in store. |
channel | text | optional | B&M | B&M or Ecom. Defaults to B&M. |
forecast_units | decimal | required | 6.400 | Zero or more. Decimals are expected and kept. |
forecast_retail | decimal | optional | 768.00 | Forecast value at retail. |
lower_bound | decimal | optional | 4.100 | Low end of your prediction interval. |
upper_bound | decimal | optional | 9.200 | High end. Must be at least lower_bound. |
launched_flag | Y/N | optional | Y | Defaults to Y. N marks pre-launch weeks, which are left out of cover calculations. |
model_version | text 64 | optional | fc-2026.07.3 | The same value on every row of the file. Two values in one file rejects it. |
generated_at | timestamp | optional | 2026-07-31T22:15:00Z | When you produced the forecast. Defaults to when we receive it. |
Uniquely identifies a row: week_start + sku_id + store_code, within one forecast version.
week_start,sku_id,store_code,channel,forecast_units,forecast_retail,lower_bound,upper_bound,launched_flag,model_version,generated_at 2026-08-01,99425703,115,B&M,6.400,768.00,4.100,9.200,Y,fc-2026.07.3,2026-07-31T22:15:00Z 2026-08-01,99425704,115,B&M,7.100,852.00,4.600,10.100,Y,fc-2026.07.3,2026-07-31T22:15:00Z 2026-08-01,99425703,704,B&M,5.200,624.00,3.300,7.500,Y,fc-2026.07.3,2026-07-31T22:15:00Z
forecast_layer — the planner’s view, if you already hold one
The forecast stack per style-colour: the engine number, the planner override, the financial plan, and which of them is being used. Send it only if your own planning system already holds overrides or plan numbers we would otherwise lose. If you do not send it, we build the same view by adding up forecast_granular, and planner edits apply on top of that.
| Column | Type | Required | Example | Notes |
|---|---|---|---|---|
week_start | date | required | 2026-08-01 | Must be a week in fiscal_calendar. |
style_color_id | text 64 | required | 994257 | Must exist in product_style_color. |
channel | text | required | B&M | B&M or Ecom. |
original_engine | decimal | optional | 112.400 | The raw engine number. |
adjusted_engine | decimal | optional | 112.400 | After your engine's own adjustments. |
multiplier | decimal | optional | 1.000 | Defaults to 1. Zero is meaningful — it suppresses the week — and is kept as zero. |
adjusted_user | decimal | optional | 120.000 | A planner override. |
mfp | decimal | optional | 118.000 | Merchandise financial plan. |
demand_source | text | optional | ENGINE | ENGINE, MFP or USER — which number is in force. Defaults to ENGINE. |
final_forecast | decimal | optional | 112.400 | The number you want planned against. |
ly_actuals | decimal | optional | 98.000 | Last year, for the comparison column. |
Uniquely identifies a row: week_start + style_color_id + channel.
price_promo — planned price and markdown
What you plan to charge, per style-colour, per channel, per week. Without it the discount panel on the forecast screen is empty: markdown still drives demand, the planner just cannot see why.
| Column | Type | Required | Example | Notes |
|---|---|---|---|---|
week_start | date | required | 2026-08-01 | Must be a week in fiscal_calendar. |
style_color_id | text 64 | required | 994257 | Must exist in product_style_color. |
channel | text | required | B&M | B&M or Ecom. |
regular_price | decimal | optional | 120.00 | |
effective_price | decimal | optional | 114.00 | After markdown. Higher than regular_price raises a warning. |
discount_pct | decimal | optional | 0.0500 | A fraction. Defaults to 0. |
promo_name | text 128 | optional | Back to School |
Uniquely identifies a row: week_start + style_color_id + channel.
Configuration files
Sourcing lanes, size curves, assortment and stocking rules.
store_dc_mapping — which centre ships to which store
Without this the allocation engine cannot pick a source and the transfer engine cannot work out a lane. Both steps skip and the Action Center is empty, so treat it as required even though it sits with the configuration files.
| Column | Type | Required | Example | Notes |
|---|---|---|---|---|
store_code | text 32 | required | 115 | Must exist in store. |
dc_code | text 32 | required | DC-CENT | Must exist in dc. |
primary_flag | Y/N | optional | Y | Exactly one Y per store. Zero or two rejects the file. |
sourcing_priority | whole number | optional | 1 | 1 is first choice. Unique within a store. |
transit_lead_time_days | whole number | optional | 2 | Centre to store. Defaults to 7. Not the same as vendor lead time. |
active_flag | Y/N | optional | Y | Defaults to Y. |
Uniquely identifies a row: store_code + dc_code.
store_code,dc_code,primary_flag,sourcing_priority,transit_lead_time_days,active_flag 115,DC-CENT,Y,1,2,Y 115,DC-EAST,N,2,4,Y 704,DC-EAST,Y,1,2,Y
size_curve — how demand splits across the size run
One row per size, with each size’s share as a fraction. You can send one curve for a style-colour everywhere, or a different curve for a store group, or for a single store — the most specific one wins when the allocator reads it.
| Column | Type | Required | Example | Notes |
|---|---|---|---|---|
style_color_id | text 64 | required | 994257 | Must exist in product_style_color. |
scope_type | text | optional | GLOBAL | GLOBAL, STORE_GROUP or STORE. Defaults to GLOBAL. |
store_code | text 32 | conditional | — | Required when scope_type is STORE, must be empty otherwise. |
store_group_name | text 128 | conditional | — | Required when scope_type is STORE_GROUP, must be empty otherwise. |
size | text 32 | required | 9 | Must be a size this style-colour actually has in product_sku. |
contribution_fraction | decimal | required | 0.237000 | A fraction. The sizes in one curve must add up to 1. |
source | text 8 | optional | USER | Defaults to USER for files you send. |
curve_version | whole number | optional | 1 | Defaults to 1. |
approved_flag | Y/N | optional | Y | Defaults to Y. Curves marked N are ignored by the allocator. |
Uniquely identifies a row: style_color_id + scope_type + store_code + store_group_name + size.
style_color_id,scope_type,store_code,store_group_name,size,contribution_fraction,source,curve_version,approved_flag 994257,GLOBAL,,,6,0.065000,USER,1,Y 994257,GLOBAL,,,7,0.129000,USER,1,Y 994257,GLOBAL,,,8,0.204000,USER,1,Y 994257,GLOBAL,,,9,0.237000,USER,1,Y 994257,GLOBAL,,,10,0.204000,USER,1,Y 994257,GLOBAL,,,11,0.108000,USER,1,Y 994257,GLOBAL,,,12,0.053000,USER,1,Y
The remaining configuration files
All optional, all the same shape as the files above, all all-or-nothing. Send them when the underlying policy exists in your systems; skip them and we fall back to the behaviour in the last column.
| Feed | What it is | Columns | Identified by | If you skip it |
|---|---|---|---|---|
product_dc_mapping | Which centre may hold which size. | sku_id, dc_code, eligible_flag | sku_id + dc_code | Every centre may hold everything, so regional exclusives get ordered into centres that never ship them. |
product_store_eligibility | Your assortment matrix, stated explicitly. | style_color_id, store_code, eligible_flag | style_color_id + store_code | Eligibility comes from your rules instead. With neither, the store dashboard is empty. |
store_group | Named groups of stores — Flagship, Metro, Outlet. One row per store per group. | group_name, store_code | group_name + store_code | Group-level size curves and rules cannot be used. |
product_status_period | Date windows when a style-colour is switched off. | style_color_id, status, status_from, status_to | style_color_id + status_from | A style is either always on or always off; no seasonal suspension. |
store_status_period | Date windows when a store is shut for a refit or move. | store_code, status (Open, TempClosed, Closed), status_from, status_to | store_code + status_from | A refitting store keeps receiving stock and reads as a stockout for weeks. |
product_supersession | This style-colour replaces that one; carry the history across. | old_style_color_id, new_style_color_id, effective_date, transfer_demand_flag, transfer_history_flag, status | old_style_color_id + new_style_color_id | Replacement colourways start with no history and trigger new-product alerts. |
new_store | Stores opening soon and the existing store they copy their plan from. | store_code, store_name, opening_date, status, action_required, sister_store_code, sister_scope_division, sister_scope_department, sister_scope_class | store_code | The New Store Setup screen is empty and an opening store gets no first allocation. |
constraint_set | Minimum display units and the weeks-of-cover band each part of the range should sit in. | name, the nine scope_ columns, min_presentation_units, max_units, target_wos, min_wos, max_wos, priority, valid_from, valid_to, active | name | Every style is held to the same defaults — 2 weeks minimum, 4 target, 8 maximum — so a clearance line and a core runner get identical cover. |
product_store_rule | Rules that decide assortment automatically: all Running to every A-tier Midwest store. | name, priority, the nine scope_ columns, five store_scope_ columns, eligible_flag, valid_from, valid_to, active | name | Assortment comes from the explicit matrix instead. With neither, nothing is eligible anywhere. |
The completion file
One small JSON file that says your delivery is finished and nothing was lost on the way.
Write the completion file (manifest) last. It tells us your delivery is finished, so we do not start loading a stock file that is still uploading, and it lets us check that nothing was lost in transit. It goes in the control folder for the business date, and it is the only JSON file in the whole contract. What we do with it on the night, and what happens when it never arrives, is in How your data flows.
{
"manifest_version": "1.0",
"workspace": "your-workspace",
"delivery_id": "5f2b6c1e-9d3a-4a7c-8f10-6b2d9e4c1a77",
"business_date": "2026-07-31",
"generated_at": "2026-07-31T23:40:12Z",
"source_system": "RETAIL-ERP",
"sequence": 20260731001,
"is_complete": true,
"files": [
{
"feed_type": "inventory_position",
"key": "inventory_position_20260731.csv.gz",
"bytes": 41533712,
"sha256": "3b1f8a0c9d47e2a5b6c8f1d0e9a2b4c6d8e0f2a4b6c8d0e2f4a6b8c0d2e4f6a8",
"row_count": 1842551,
"encoding": "UTF-8",
"compression": "gzip",
"mode": "full",
"extract_watermark": "2026-07-31T22:00:00Z"
},
{
"feed_type": "sales_weekly",
"key": "sales_weekly_20260731.csv.gz",
"bytes": 9822104,
"sha256": "a7c3e1b5d9f2048a6c8e0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a",
"row_count": 412887,
"encoding": "UTF-8",
"compression": "gzip",
"mode": "restate",
"restate_from": "2026-07-18",
"restate_to": "2026-07-31"
},
{
"feed_type": "purchase_order",
"key": "purchase_order_20260731.csv",
"bytes": 2210344,
"sha256": "c1e5a9d3b7f0246e8a0c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d2f4a6c8e0b2d4f",
"row_count": 18422,
"encoding": "UTF-8",
"compression": "none",
"mode": "full"
}
]
}| Field | Required | What it means |
|---|---|---|
manifest_version | required | Always 1.0 today. |
workspace | required | Your workspace name, exactly as it appears in your folder path. A mismatch stops the whole delivery. |
delivery_id | required | Any unique value per delivery. Send the same one twice and we record it and do nothing — no double loading. |
business_date | required | The day this delivery is for. Where it disagrees with the data, the data wins and we raise a warning. |
generated_at | optional | When your export finished. |
source_system | optional | Whatever you call the system it came from. It goes in the audit trail. |
sequence | optional | Increment it if you send more than one delivery in a day. |
is_complete | required | false lets you stream a big delivery in pieces without us starting early. We wait for true. |
files[].feed_type | required | The feed name, exactly as on this page. |
files[].key | required | The file name you uploaded. |
files[].row_count | required | Data rows, not counting the header. See below. |
files[].sha256 | required | Checksum of the file as uploaded. See below. |
files[].bytes | required | Size of the file as uploaded. |
files[].encoding | optional | UTF-8. |
files[].compression | optional | gzip or none. |
files[].mode | required | full, delta or restate — see below. |
files[].restate_from / restate_to | conditional | Required when mode is restate. The window your file replaces; the rows in the file must actually fall inside it. |
files[].extract_watermark | optional | The moment your extract read the source system. Useful when a number is questioned months later. |
Why the row count and the checksum are there
They are a safety net, not paperwork. The most common real-world failure is not a badly formatted file — it is a truncated one. A stock extract that dies two-thirds of the way through still parses perfectly, still passes every column rule, and quietly tells us that a third of your estate has no stock. Nothing inside the file can catch that. A row count and a checksum can, in one comparison, before a single row is loaded. If either disagrees we load nothing and tell you, rather than half-loading a day.
The three delivery modes
- full
- The file is the complete current picture for that feed. Anything absent from it is zeroed (stock) or retired (masters), never left behind.
inventory_positionmust always be full. - delta
- Only what changed. Anything absent is left exactly as it was. Safe for masters, never accepted for stock.
- restate
- The file replaces a bounded window completely — used for
sales_weekly,price_promoandforecast_granular. State the window inrestate_fromandrestate_to.
Validation rules and what they mean
What we reject, what we warn on, what we correct for you.
Three outcomes are possible for anything that does not validate: the row is rejected and the rest of the file loads, the whole file is stopped, or we load it and tell you. Which one applies depends on the file, and it is chosen so that a small operational mess never costs you a day and a systematic mistake never loads quietly.
| Situation | Outcome | Why |
|---|---|---|
| A sales, stock or forecast row names a size or store we have never seen | row rejected | Inventing a placeholder product would give it a blank hierarchy, and every division and class total on the dashboard would then leak units into an unnamed bucket. Because these files are complete or restated, the row comes back by itself on the next delivery once the master catches up. |
| A stock row names a location code that is not one of your stores or centres | row rejected | Otherwise the position simply vanishes from every store screen with no error anywhere. |
| A week in a sales, price or forecast file is not in your calendar | row rejected | A one-off bad week is a data error, not a reason to lose the file. |
| An order line names a vendor we do not know | loaded, with a warning | An order with no vendor label is still an order. Losing the inbound units would be worse than losing the name. |
| An order line names a distribution centre we do not know | row rejected | There is nowhere for the stock to arrive, so there is no usable row. |
| More rows fail than the file's error budget allows, usually all on the same rule | file stopped | That is a systematic export problem — every week starting on a Monday when your weeks start on a Saturday — not a scattering of bad rows. Loading the survivors would look like a normal day. The budget per file is listed below. |
| A master or configuration row references something that does not exist | file stopped | These files define the identity space everything else joins to. A half-loaded product list makes every roll-up wrong with nothing on screen to say so. |
| A store has no primary distribution centre, or two of them | file stopped | Allocation would be non-deterministic — the same night could source the same store differently. |
| Status windows for one store or style overlap, or a supersession chain loops | file stopped | Two contradictory statuses on one date cannot be resolved, and a loop would never finish. |
| A complete master file would retire more than 10% of your live products or stores | file stopped | Almost always a truncated extract rather than a real range cull. We ask you before acting on it. |
| The row count, size or checksum does not match the completion file | file stopped | A truncated or corrupted upload. Never partly loaded. |
| Columns we do not recognise | ignored, listed back | You add fields to your extracts all the time. Treating that as a failure would train everyone to ignore alerts. |
| Negative on-hand stock | loaded, with an alert | It is a real till condition, not a typo. We load it and raise it if it becomes common. |
What we correct for you
- Header names are trimmed, lower-cased, and spaces become underscores — so
Week_Startandweek startboth land onweek_start. Column order never matters. margindefaults tonet_salesminuscogswhen both are present.style_color_idon an order line is filled in from the size when you leave it empty;line_statusdefaults to the order status.- Yes/no columns accept
true,false,1,0,YesandNoin any case. - A byte-order mark at the start of the file is stripped rather than corrupting your first column name.
How many bad rows are too many
Each file has an error budget. Below it, the bad rows are rejected and the file loads; above it the whole file is held, because at that point the file is telling you something about your extract rather than about your business.
| File | Budget | Reasoning |
|---|---|---|
inventory_position | 0.5% of rows | The stock position is the decision surface. One row in 200 wrong is already too many. |
sales_weekly | 2% of rows | Late product setup is normal and self-heals on the next restatement. |
purchase_order | 2% of rows | Same. |
forecast_granular | 2% of rows | Same. |
price_promo | 2% of rows | Same. |
product_store_eligibility | 2% of rows | An assortment matrix is transaction-sized; one retired style must not block it. |
size_curve | 5% of curves | Counted per curve, not per row, because a curve is only meaningful whole. |
| Master and configuration files | Zero | All-or-nothing by design. |
The volume check
This is the one that saves the most days. We compare each file’s row count against the median of your last eight successful deliveries of that feed. If it is more than 40% away in either direction, we hold the file and ask you to confirm before loading it. Nothing is rejected and nothing is lost — it simply waits for a yes.
Getting the rejected rows back
Every delivery produces a rejects file you can read from your folder, one row per rejection, with the row number, the column, the value we saw, the reason, and where we can work it out, a suggestion — "0115" did you mean 115 (Riverside Mall)? Most rejects turn out to be a leading zero, a date format, or a product that has not been set up yet, and the suggestion column usually resolves them without anyone opening a support ticket.
Download templates
A complete, consistent example set. Every row below is real and loads end to end.
These are the exact files described above, for a small footwear range: five style-colours, the full size 6 to 12 run for 994257, six stores, three distribution centres and three vendors. Every transactional row references only masters that are present in the same set, so you can load the whole thing end to end and see what a good delivery looks like before you write a line of export code.
| File | Rows | What is in it |
|---|---|---|
| fiscal_calendar.csv | 7 | Weeks 13 to 19 of 2026, Saturday to Friday. |
| dc.csv | 3 | DC-EAST, DC-CENT and DC-WEST. |
| store.csv | 6 | Five shops and the e-commerce channel as a store. |
| vendor.csv | 3 | Three suppliers with incoterms. |
| product_style_color.csv | 5 | Men's, women's and kids' running. |
| product_sku.csv | 7 | The complete adult size run 6 to 12 for 994257. |
| vendor_style_terms.csv | 5 | Lead time 21 days, minimum 48, packs of 6. |
| store_dc_mapping.csv | 7 | Includes a secondary centre for store 115. |
| size_curve.csv | 7 | One curve for 994257 that adds to exactly 1.000000. |
| inventory_position.csv | 6 | Store and centre rows, with 994257 engineered as a stockout at store 115. |
| sales_weekly.csv | 6 | Two weeks, with sales, cost and margin that reconcile. |
| purchase_order.csv | 6 | Order and line columns in one file, including a received and a pending order. |
| forecast_granular.csv | 6 | Two forward weeks from one forecast version. |
| _manifest.example.json | 3 files | The completion file, with checksums, row counts and modes. |