Plenty of vendors will never give you an SFTP login — they email a price list, or hand you a workbook at a trade show. A spreadsheet feed is the same catalog importer as an automated feed, with a different front door: you send the file yourself instead of us going to fetch it.
When to use a spreadsheet feed
Use it whenever the vendor’s catalog arrives as a file rather than at an address. Everything downstream is identical to an SFTP Product Feed — the same column mapping, the same brand rules, the same category levels, the same matching strategy, and the same Catalog review queue. The only differences are on the delivery side:
| Spreadsheet upload | SFTP product feed | |
|---|---|---|
| Where the file comes from | You upload it | We poll the vendor’s server |
| Connection & credentials | None needed | A vendor connection on the vendor |
| Schedule | None — it runs when you upload | Every n minutes |
| Primary button on the feed row | Upload files | Run feed / Run again |
| Enable/disable toggle | Not shown — an upload is the run | Yes, unlocked after a first run |
| Dry run | Not offered | Offered on inventory feeds only |
| File formats | .xlsx workbook, comma CSV, tab-separated .tsv |
Whatever the vendor drops (comma, pipe, tab, semicolon) |
Spreadsheet feeds are product catalog feeds. Inventory (stock quantity) feeds are SFTP-only — the “Spreadsheet upload” choice only appears when you add a feed from the Products section of the vendor’s Integration tab. The Inventory section’s + Add Feed offers SFTP alone.
ℹ️ Note: A vendor can hold as many feeds as it needs. An item master, a separate spec/copy file, a MAP price list that arrives weeks apart, and an inventory feed can all sit on the same vendor at the same time — as several spreadsheet feeds, several SFTP feeds, or a mix. There is no one-feed-per-type limit, so you never have to split a supplier into two vendor records to get a second feed.
📷 Screenshot: The “Add Product Feed” delivery picker opened from the Products panel’s + Add Feed, showing both choices — “Spreadsheet upload — Upload an Excel or CSV by hand — no connection needed.” and the SFTP option beneath it. (placeholder — replace with
/docs-images/vendors/spreadsheet-uploads-add-feed-picker.png)
What we can read
| Format | Supported | Notes |
|---|---|---|
.xlsx |
Yes | Only the first worksheet in the workbook is read. Formula cells use their calculated result; date cells come through as YYYY-MM-DD; rich-text and hyperlink cells are flattened to their text. |
.csv |
Yes | Must be comma-separated. Quoted fields, embedded commas and line breaks inside quotes are all handled. |
.txt |
Yes | Read as a comma-separated file — the extension changes nothing about the delimiter. |
.tsv |
Yes | Read as tab-separated. The file picker’s suggested types are .xlsx, .xls, .csv and .txt, so you may have to switch the dialog to show all files to pick a .tsv. |
.xls (legacy Excel) |
No | The file picker offers it, but an old binary .xls is not a workbook we can open and is not a delimited text file either — its contents won’t split into columns. Open it in Excel and re-save as .xlsx or CSV first. |
A few mechanics worth knowing before you send a file:
- Everything above the header row is thrown away. Vendor title banners, logos and “Price list effective…” rows are fine — you just tell us which row the column names are on.
- Completely blank rows are skipped, and any column whose header cell is empty is ignored.
- Every value is trimmed of leading and trailing spaces.
- 60 MB per sheet is the ceiling, checked per file. A 70,000-row catalog is comfortably inside it.
💡 Tip: Pipe- and semicolon-delimited files are not readable on the upload path — they arrive as one jammed-together column. Re-save them as
.xlsxor re-export as comma CSV. (Those delimiters are selectable on an SFTP feed, which is another reason a vendor with an odd delimiter is easier to run over SFTP.)
Create the spreadsheet feed
You do this once, with a sample of the vendor’s sheet, so we can read its real column headers and you can map them. The sample is never stored — it’s read in memory, used to fill the pickers, and discarded.
- Open the vendor — go to Purchasing → Vendors, click the vendor, then open the Integration tab.
- Add the feed — in the Products panel, click + Add Feed, then choose Spreadsheet upload — Upload an Excel or CSV by hand — no connection needed.
- Name it — Label is the first field in the New Spreadsheet Feed modal. Give the feed a name you’ll recognise on the row, e.g.
VSG catalog (upload). Leave it blank and the feed is calledSpreadsheet catalog. - Upload the sample — under Main product sheet, pick the vendor’s file. We read the first 20 rows, guess which of the first 12 rows holds the column names (the one with the most filled cells), and report Detected: N columns.
- Correct the header row if needed — Header is on row is a plain row number. Change it and the column list is re-read from that row of the same sample.
- Map the columns — pick the Vendor part-number column (required), then confirm the five optional field pickers. We pre-fill them from common header names, so this is usually confirmation rather than assignment.
- Set the category column — choose the vendor’s own grouping column under Category / product group. This is deliberately separate from the field mapping above: it becomes the vendor’s category levels, which is what the category mapping screen lists.
- Add a pricing sheet if there is one (see the next section), set the title template, and click Create feed.
📷 Screenshot: The New Spreadsheet Feed modal after a sample has been read — the “Header is on row” field showing “Detected: N columns”, and the Map columns section with the Vendor part-number picker and the five mapping dropdowns populated with real vendor headers. (placeholder — replace with
/docs-images/vendors/spreadsheet-uploads-new-feed-mapping.png)
⚠️ Warning: The wizard only ever sees the sample’s first 20 rows. If the real header row sits below row 20, the column list comes back empty and there is nothing to map. Trim the banner rows off the top of the sample before uploading it — the real file you send afterwards can be as tall as you like.
What you set at setup
| Field | Required | What it is |
|---|---|---|
| Label | No | The name on the feed row. Defaults to Spreadsheet catalog. |
| Vendor part-number column | Yes | Their item number, and the permanent link between their row and your product. Later uploads recognise rows you’ve already decided on by this number, so it must be stable from file to file. |
| Product name | No | The short name used to build titles. |
| Brand | No | The brand column. Also the default source for brand resolution on this feed. |
| UPC / barcode | No | Strongly recommended — the feed is created matching on UPC. |
| Your cost | No | Your stocking cost from this vendor. |
| List / retail price | No | Their MSRP / list price. |
| Category / product group | No | The vendor’s own grouping column. Its values become the vendor groups you map to your categories after the first run. |
| New product titles | No | Template for titles on products this feed creates. Tokens: {brand}, {mpn}, {name}. Blank pieces drop out. Defaults to {brand} {name}. |
⚠️ Warning: Map generously the first time. Later, when you reopen the feed with Edit feed, the column pickers can only offer the columns you mapped at setup — an upload feed has no endpoint to re-read a sheet from, so there is no “read columns” button there and a fresh upload does not extend the list. A column you skipped is easiest to pick up by adding a second spreadsheet feed on the same vendor for the same file, mapping the extra columns there. (Several feeds on one vendor is fully supported — see the note above.)
How the feed is created
The wizard writes sensible defaults so a first upload can’t do anything drastic. Everything in this table except Review is editable afterwards from ⋯ → Edit feed.
| Setting | Created as |
|---|---|
| Feed type | Product Catalog |
| Delivery | Upload — no connection, no schedule |
| Enabled | Off, and it stays off — an upload feed has no schedule to enable, and a catalog run that only stages proposals is allowed to run on a disabled feed |
| Review | Everything is staged for review; nothing is written to your catalog by a run |
| Create missing products | On (still staged — a new product is created from Catalog review, never by the run) |
| Matching strategy | UPC / barcode |
| SKU for new products | Your own number series |
| Images | Linked from the vendor rather than downloaded |
| Brand | The column you mapped to Brand; brands that are a bare number, or 0, are rejected |
| Title template | What you typed, or {brand} {name} |
ℹ️ Note: “Everything is staged for review” is not a switch in the dashboard — there is no review-mode control on the feed editor. Catalog feeds stage by default and behave that way for everyone; if you have a case that genuinely needs a feed to write through without review, that is a change ILLUMA support makes for you.
Creating the feed is configuration only — nothing has been read yet. Save it and the upload dialog opens straight away so you can send the real file.
The MAP / pricing sheet and the join key
Many vendors ship MAP or advertised pricing in a second sheet, keyed by the same part number, often on a completely different schedule. Rather than making you merge the two by hand in Excel every time, the feed can hold a second file slot and do the join for you.
In the MAP / pricing sheet section of the setup wizard:
- Upload a sample of the pricing sheet — its columns are read the same way.
- Header is on row — the pricing sheet’s own header row.
- Join on — the column the two sheets share, chosen from the pricing sheet’s headers. We default it to the first header that looks like a part/item/SKU column, and fall back to its first column.
- MAP price column — the column carrying the MAP price. This is the column that gets merged in, and it’s mapped to the MAP Price field for you.
⚠️ Warning: The second slot is only added to the feed if you pick both a pricing file and a MAP price column. Choose the file but leave the MAP price column on “— none —” and the feed is created with a single sheet — no pricing picker will appear on the upload dialog. If that happens, recreate the feed (or add a second feed for the price list).
📷 Screenshot: The “MAP / pricing sheet (optional)” section with a file chosen and the three-field row visible — Header is on row, Join on, MAP price column. (placeholder — replace with
/docs-images/vendors/spreadsheet-uploads-pricing-join.png)
How the join actually behaves:
- The join key’s values are matched case-insensitively and trimmed, so
AB-1234andab-1234are the same part. - The join column must exist in both sheets under the same header name (case and surrounding spaces don’t have to match, but the wording does). If the main sheet calls it
Item #and the pricing sheet calls itSKU, nothing will match — rename one before uploading. - Only the MAP price column is merged across. The rest of the pricing sheet is ignored.
- If the pricing sheet lists the same part more than once, the last row wins.
- If the merged column’s name already exists in the main sheet, it’s added as
<name> (pricing)so nothing is silently overwritten. - Pricing rows with no match in the main sheet are simply dropped, and main rows with no pricing row keep an empty value.
ℹ️ Note: The main product sheet is what the pricing sheet is joined onto. A pricing sheet on its own has nothing to attach to and can’t be uploaded alone. If you want to load a price list by itself, give it its own spreadsheet feed on the same vendor, where the price list is the main sheet.
Upload the files
- Open the feed — on the vendor’s Integration tab, find the feed in the Products panel and click Upload files. (It’s also under ⋯ → Upload files.)
- Pick the main product sheet — required. If the feed has a pricing slot, you’ll see a second, optional MAP / pricing sheet picker beneath it.
- Click Upload & analyze — the progress bar is the real transfer; the files go straight from your browser to storage, so a large catalog doesn’t crawl. When the bar fills, the message changes to Reading the spreadsheet and staging it… while the sheets are fetched back, parsed and joined.
- Read the confirmation — you’ll get Staged N rows, plus M matched a pricing row when a second sheet was included, then Analyzing now — it’ll appear in the review queue shortly.
- Click Done — the feed row switches to Run queued — waiting for the worker, and the page refreshes itself every 15 seconds until the run finishes.
📷 Screenshot: The Upload files dialog mid-upload — both file slots filled (“Main product sheet” and “MAP / pricing sheet”), the progress bar partly filled, and the stage line reading “Uploading pricing sheet… 62%”. (placeholder — replace with
/docs-images/vendors/spreadsheet-uploads-upload-modal.png)
You do not have to send both sheets every time. Secondary sheets are optional on each upload — send the main sheet alone when only the catalog changed, and send both when the vendor reissues MAP.
What happens after you upload
- The sheets are parsed and joined. The workbook (or CSV) is read using the header row you set, the pricing sheet is joined on the key, and the result is written as one combined CSV.
- That CSV is staged against the feed as an uploaded file, named after your main sheet.
- A run is requested. The feed’s last error is cleared and the run is queued.
- The worker picks it up on its next pass — it polls about once a minute — and promotes the staged file into an import job. From here, the file is indistinguishable from one pulled off a vendor’s SFTP server.
- The import runs. Rows are matched to your products by the feed’s matching strategy, brand rules and skip lists are applied, the vendor’s category values are collected, and results are staged as proposals.
- Everything lands in Catalog review. Confident matches under To approve, ambiguities and unrecognised items under Needs a decision, and new products whose brand looks suspect under Flagged.
✅ Nothing an upload does touches your catalog on its own. A run stages proposals; products and their vendor products (the alias that ties a vendor part number to one of your products) are only written when you approve and apply them in Catalog review.
When the run finishes, the feed row shows a Last upload badge with the row count and a stat grid, plus a Review N to review → button straight into the queue. matched, created and need review are always shown; flagged, not carried, filtered out, vendor categories and failed appear only when they’re above zero.
Two of those numbers read oddly on a staging run, and both are expected:
- matched counts matches written straight through. Because a spreadsheet feed stages everything, a confident match is filed as a proposal instead — so matched can sit at 0 while the To approve tab fills up. Trust the review queue’s counts, not this tile.
- not carried only counts rows skipped because product creation is off. Spreadsheet feeds are created with it on, so this tile normally stays hidden.
ℹ️ Note: The Review N to review → button counts the vendor’s whole pending queue — needs-a-decision plus flagged, across every feed on that vendor. The stat grid above it is this run’s numbers only. That’s deliberate: a queue is a vendor-wide worklist, while a run’s tallies belong to the run.
📷 Screenshot: A completed spreadsheet feed row showing the “Product Catalog” and “Upload” badges, the “Main + pricing, joined on <key>” meta line, the Last upload stat grid, and the “Upload files” and “Review … →” buttons. (placeholder — replace with
/docs-images/vendors/spreadsheet-uploads-feed-row-after-run.png)
⚠️ Warning: Only one run per feed can be queued at a time. If you upload again while a run is still waiting to be picked up, you’ll get A run is already queued for this feed — let it finish first. Once the worker has claimed the run, a further upload is accepted and simply waits its turn — staged files are promoted oldest first.
Sending updated files later
Uploading is the update mechanism — there’s no schedule to wait for and nothing to enable.
- A new price list or catalog refresh? Click Upload files and send it. The vendor part number is the identity, so rows that already matched are re-proposed with the new values, and anything genuinely new is staged for review.
- Changed the mapping, brand rules or category columns? You do not need to re-upload. ⋯ → Run again re-reads the file you last uploaded — the staged copy is kept in storage and put back in play for you — so a config change reaches your data without the vendor sending anything new.
- Re-uploading the same file also works and does the same thing, one run per upload. Use it when you actually have a corrected file.
- Fresh re-analyze (under ⋯) clears the pending review queue first, then runs.
- Re-title products (under ⋯) re-applies the current title template to products this feed already created — the backfill after a template change. It runs as a background job you can watch in the jobs tray.
⚠️ Warning: Fresh re-analyze clears everything currently waiting for review on the whole vendor — To approve, Needs a decision and Flagged, across every feed on that vendor, not just this one. Products you’ve already created and applied are left alone, and human rejections are kept. Use it when the pending queue is stale rather than partly decided.
Editing the feed later
⋯ → Edit feed opens the same feed editor an SFTP feed uses, minus the parts that only exist for a server: no directory, no archive directory, no filename pattern, no delimiter, no poll schedule. The Delivery field reads Spreadsheet upload — no connection. Everything that decides what the importer does with your rows is here: matching strategy, column mapping, brand sources and rejects, SKU and brand skip lists, row filters, new-product gates, category levels, image handling, SKU source, drop-ship mode, title template, and what the feed may overwrite on a product it already matched.
Two things behave differently here than they do on an SFTP feed, both because there is no endpoint to read a live sample from:
- The column dropdowns list only the columns you mapped at setup — the part-number column, the fields you mapped, and the MAP price column if you added one. There is no “Read columns from the vendor” button.
- The Categories level pickers draw from that same short list. If the category column you chose in the wizard isn’t among your mapped columns, its picker can look blank even though the setting is still saved. Leave it alone unless you mean to change it.
⋯ → Category mapping is where the vendor’s own product groups — collected from the column you set as Category / product group — get mapped onto your category tree. Run the feed once first; there are no groups to map until a file has been read.
Troubleshooting
| What you see | What it means |
|---|---|
| Could not read column headers from “file”. Check the header row setting. | The header row number points at a blank or banner row. Reopen Edit feed, or check the row number you set at setup against the file you just sent. |
| No data rows found in “file” below the header. | The header row is set below the actual data, or the sheet has headers only. |
| Missing the main product sheet | Only a secondary sheet was chosen. The main product sheet is required on every upload. |
| the uploaded file is empty | A zero-byte file. Re-export it from Excel. |
| larger than 60MB | Split the file, or re-save a CSV as .xlsx (a workbook is usually far smaller). |
| could not read the uploaded file (HTTP …) | The transfer to storage didn’t complete, or the object isn’t readable yet. Retry the upload. |
| unexpected file location | The upload didn’t land in ILLUMA’s own storage — almost always a browser or extension interfering mid-upload. Retry in a clean window. |
| A run is already queued for this feed — let it finish first | An earlier upload hasn’t been picked up yet. Wait for the row to move past Run queued. |
| This feed is not configured for uploads | The feed is an SFTP feed. Add a separate spreadsheet feed on the same vendor instead. |
| The wizard reads no columns at all | The real header row is below row 20 — the sample preview stops there. Trim the banner rows off the sample. |
| Columns are all jammed into one | A pipe- or semicolon-delimited file was uploaded with a .csv/.txt extension, or a legacy .xls. Re-save as .xlsx or as comma CSV. |
| Half the catalog is missing | An .xlsx with the data on the second tab. Only the first worksheet is read — move the sheet or re-save it as its own workbook. |
| MAP prices didn’t come through | The join key isn’t spelled the same in both sheets, the pricing sheet’s key column is blank on those rows, or the feed was created without a MAP price column so it has no pricing slot at all. |
| The feed row still says Run queued after several minutes | Normal for a large catalog — the worker claims the request within about a minute, then the import itself takes a while. The page gives up watching after 20 minutes; reload and check the row’s error line. |
Next
Work the results in Catalog review on the vendor — approve what’s right, reject what isn’t, then create and apply. After the first run, set up Vendor Categories so this vendor’s groups file into your own tree. If the vendor later gives you a server login, the same importer runs unattended over an SFTP Product Feed — and stock levels need their own Inventory Feed. Anything still not behaving is covered in Vendor Troubleshooting.
