Skip to content

Bulk upload

Import hundreds or thousands of records at once from a CSV file, instead of typing them in one at a time. Bulk upload handles four entity types: LPNs, locations, products, and receiving orders.

Before you begin

You need an admin or manager role with bulk upload permissions. Select your target warehouse from the warehouse selector first, and download the CSV template for the entity type you're importing.

When to use bulk upload

Situation Use bulk upload for
Setting up the system Importing an existing product catalog
Onboarding new inventory Creating receiving orders for stock on the way in
Onboarding LPNs Registering pallets and containers in bulk
Setting up locations Creating warehouse locations in bulk
Migrating data Moving records over from a legacy system
Seasonal changes Adding new products for seasonal inventory quickly

Accessing bulk upload

Bulk Upload Main Interface

  1. Select Administration in the left sidebar.
  2. Select Bulk Upload from the submenu.

The bulk upload interface opens, showing entity type selection, your upload history (if you have any), and a job status tracking panel.

Supported entity types

Bulk upload supports four entity types. These are the complete set — the Data Type dropdown in the upload modal offers exactly these four, in this order, and defaults to LPNs:

Entity Type Description Common Use Cases
LPNs License plate numbers with type, status, and current location Pallet and container onboarding, staging setup
Locations Warehouse locations with zone assignments Warehouse setup, location expansion
Products Product catalog entries with SKUs, names, barcodes Initial catalog setup, new product launches
Receiving Orders Purchase orders with expected inventory Inventory receiving preparation

There is no bulk upload for customers, inventory adjustments, or serial numbers. Serial numbers are imported as a column on the receiving orders upload rather than on their own — see Serial-tracked products.

Calling the API directly

GET /bulk-upload/entity-types returns only the four types above. The BulkUploadEntityType enum also defines inventory, orders, users, and warehouses, but no processor is registered for them - a job submitted for one of those passes request validation and then fails with PROCESSOR_NOT_FOUND. Treat the endpoint's response as the authoritative list, not the enum.

Upload process

1. Select entity type

Bulk Upload Modal

  1. Select New Upload.
  2. Select the Entity Type from the dropdown.
  3. Review the guidelines shown for that type.

2. Upload your CSV file

You can start the upload three ways:

Method How it works
Drag and drop Drag your CSV file into the upload zone
File browser Select Browse files to pick a file from your computer
Document extract Upload a PDF, image, or other document, and the system extracts structured data to pre-populate the upload fields

The file has to meet these requirements:

Requirement Value
Format CSV, or a supported document format for extract
Maximum size 10MB per file
Maximum rows 2,000 data rows per upload — see Row limit and skipped rows
Character encoding UTF-8
Column headers Must match the template exactly (CSV uploads)

Download template

Select Download Template in the upload modal to get a pre-formatted CSV with the correct column headers and sample data.

3. Validation

The system checks your file before processing it:

Check What it confirms
File format The CSV structure is valid
Column headers The required columns are present
Data types Field formats are correct — numbers, dates, emails
Business rules No duplicate SKUs or barcodes, no invalid warehouse references, no serial number conflicts, no missing required fields
Result Meaning
Success The file passes every check and moves to processing
Warnings Minor issues found — you can continue or fix them first
Errors Critical issues found — the upload is rejected, with a detailed error report

4. Processing

Once your file passes validation, the system creates a background job, processes each row in sequence, and logs any row-level errors as it goes. When it finishes, it generates a summary report.

Status Meaning
Pending Job queued for processing
Processing Actively importing records
Completed The job finished. Some rows may still have failed
Failed No rows imported — every row failed, or a critical error stopped the job
Cancelled You cancelled the job before it finished

Completed doesn't mean every row imported

A job that imports 40 rows and rejects 10 still ends as Completed. There's no separate partial status. Check the success and error counts on the job, and download the error report, before you assume the whole file landed.

Row limit and skipped rows

A single upload processes at most 2,000 data rows (the header row doesn't count). The limit applies the same way to CSV uploads and to rows extracted from a document.

Files larger than the limit are not rejected, and the extra rows are not discarded. The job processes the first 2,000 rows and records every remaining row as a Skipped result, so you can see exactly which records weren't captured and finish the import in a second upload.

A file over the limit imports partially

Uploading a 3,000-row file creates 2,000 records, not 3,000. The upload still reports success for the rows it processed, so check the skipped-rows notice before assuming the whole file landed.

What you see

When an upload exceeds the limit:

  • A warning notification appears: "N rows not imported"
  • An amber notice on the results view states how many rows the file contained, how many were processed, and which row numbers were skipped - for example, "This file has 3,000 data rows, but a single upload processes at most 2,000. The first 2,000 rows were processed; data rows 2,001 through 3,000 were skipped."
  • Each skipped row appears in the results table with the status Skipped, keeping its original row number from your file
  • The jobs list shows a Skipped (row limit) count on the job

The same notice appears later on the job's detail view, so you can return to a past upload and still recover its skipped rows.

Skipped rows count toward progress

A job's progress percentage counts skipped rows as accounted for, so a partially imported file still reaches 100%. The Processed count stays an honest count of rows actually handled.

Finishing the import

  1. Open the upload results — from the upload modal, or by selecting the job in the jobs list.
  2. Select Download skipped rows (CSV) in the amber notice, or Skipped Rows (Re-upload) in the Download Results panel.

The file downloads as <your-filename>-skipped-rows.csv, using the same columns and order as the upload template.

  1. Upload that file as-is for the same entity type. No editing required.
  2. Repeat if the remainder is still over 2,000 rows.

Split large files up front

If you know a file exceeds 2,000 rows, splitting it into batches before uploading avoids the download-and-re-upload round trip entirely.

Downloading skipped rows requires bulk upload export permissions. The download is only available for jobs the row limit actually truncated.

CSV file requirements

General format rules

All CSV files must follow these standards:

column_name_1,column_name_2,column_name_3
value_1,value_2,value_3
value_1,value_2,value_3
Rule Detail
Header row First row must contain column headers, an exact match to the template
Separator Commas
Text with commas Enclose it in double quotes
Optional fields Leave the cell empty — don't use "null" or "N/A"
Dates Use YYYY-MM-DD
Booleans true/false or 1/0

Columns the template doesn't define are ignored, not rejected

Header validation fails only on a missing required column or a duplicate header. A column name the platform doesn't recognize produces a warning — "Unexpected columns (will be ignored)" — and the upload proceeds without it. A misspelled or invented header therefore imports a row with that value silently dropped rather than telling you the file is wrong. Start from the downloaded template rather than typing headers by hand.

Products CSV template

sku,name,description,barcode,unit_of_measure,weight,reorder_point,reorder_quantity,is_lot_tracked,is_serial_tracked
WIDGET-001,Blue Widget,Standard blue widget for testing,123456789012,EACH,1.5,10,50,false,false
DEVICE-002,Smart Device,IoT-enabled monitoring device,987654321098,EACH,2.0,5,20,false,true

Required fields:

  • sku: Unique product identifier. Uppercase letters, numbers and hyphens only (for example WIDGET-001)
  • name: Product display name

Those two are the only required columns — every other column below may be omitted entirely.

Optional fields:

Group Columns
Description description, barcode, image_url
Packaging and dimensions unit_of_measure, weight, length, width, height
Inventory management reorder_point, reorder_quantity, max_stock
Pricing cost, price
Tracking is_lot_tracked, is_serial_tracked, is_active
Handling and storage is_hazmat, temperature_requirement, storage_class, handling_requirements, preferred_location_type, max_stack_height, fragile

Values the template suggests for the constrained columns:

  • unit_of_measure: EACH, CASE, BOX, PACK, PALLET
  • temperature_requirement: AMBIENT (room temperature), REFRIGERATED (35–45°F), FROZEN (below 32°F)
  • preferred_location_type: FLOOR, RACK, SHELF, BULK
  • storage_class: user-defined (for example A, B, FRAGILE). Must match a location's storage class restriction for putaway to consider that location
  • Boolean columns accept true/false, yes/no or 1/0

There is no category column

Products are organized by storage_class and preferred_location_type, not by a free-text category. A category header is one of the unrecognized columns the uploader drops.

Receiving orders CSV template

When you upload receiving orders, they become visible to your entire warehouse team, so several people can process different POs from the same import at once.

Each row is one line item. Header-level columns (the purchase order, carrier and equipment details) repeat on every row of the same order, or are left blank after the first row.

purchase_order_number,customer_name,carrier_name,expected_date,carrier_reference,product_sku,expected_quantity,unit_cost,lot_number,serial_number
PO-001,Acme Corp,FedEx,2024-01-20T10:00:00Z,TRUCK-ABC123,APP-SHIRT-001,100,8.99,LOT-001,
PO-001,,,,,ELEC-LAPTOP-001,1,1299.99,,SN-12345678
PO-002,Tech Solutions Inc,,2024-01-21T14:00:00Z,,ELEC-PHONE-001,25,899.99,,

Required fields:

  • product_sku: Must match an existing product SKU in the warehouse
  • expected_quantity: Number of units expected to receive

Those two are the only required columns. In particular purchase_order_number is optional — see the grouping note below for what happens when you leave it out.

Optional fields:

Level Columns
Purchase order purchase_order_number, customer_name, carrier_name, priority, expected_date, carrier_reference, tracking_number, notes, assigned_to_email
Equipment and paperwork expected_seal, equipment_type, equipment_size, equipment_number, mbol_number, bol_number
Line item lot_number, serial_number, expiration_date, unit_cost, unit_weight, line_notes

Values accepted by the constrained columns:

  • priority: LOW, NORMAL, HIGH, URGENT
  • equipment_type: TRAILER, CONTAINER, FLATBED, FLAT_RACK, LOWBOY, CHASSIS
  • equipment_size: 20, 40, 45, 53
  • expected_date: ISO 8601 with time (YYYY-MM-DDTHH:MM:SSZ)
  • expiration_date: date only (YYYY-MM-DD)
  • mbol_number and bol_number: maximum 50 characters

Grouping and validation:

  • Rows are grouped into one receiving order by purchase_order_number together with carrier_reference — the same PO arriving on two different carrier references becomes two receiving orders.
  • Rows with no purchase_order_number are not grouped with each other; each becomes its own receiving order under a generated placeholder reference. Supply the column whenever you want multiple line items on one order.
  • The system checks that every product_sku value exists before processing.

po_number is not the column name

Earlier versions of this page named the grouping column po_number. The platform reads purchase_order_number. Because unrecognized headers are ignored rather than rejected, a file using po_number uploads successfully and silently produces one receiving order per line item, with no purchase order number recorded.

Job history and permissions:

Receiving order uploads are retained permanently — you can review historical imports for audit purposes, see who uploaded which POs and when, and check upload status and validation errors. Admins and managers can upload receiving orders; all warehouse staff can view and process the uploaded POs, and the system tracks which user processed each one.

Serial-tracked products

Serial numbers have no entity type of their own. They are imported as an optional serial_number column on the receiving orders CSV, and the platform validates them while it validates the rest of the row.

Serial numbers on a receiving orders upload

purchase_order_number,product_sku,expected_quantity,serial_number
PO-2025-001,DEVICE-002,1,SN2025-001-0001
PO-2025-001,DEVICE-002,1,SN2025-001-0002
PO-2025-001,DEVICE-002,1,SN2025-001-0003

One row per unit. When a serial number is supplied, expected_quantity must be 1. A serial-tracked product with a quantity above 1 fails with "must have expected quantity of 1 when serial number is provided" - so three units of DEVICE-002 need three rows, as above.

Serial number validation

For products saved with serial tracking enabled:

Condition Result
Product is serial-tracked and serial_number is empty Error - "Serial number is required for serial-tracked product"
serial_number supplied with expected_quantity other than 1 Error - quantity must be 1
Same serial number and SKU appear twice in the file Error - names the earlier row number
Serial number already exists for that SKU in the system Error - includes the existing record's status
serial_number supplied for a product that is not serial-tracked Warning - the row imports and the serial number is ignored

Uniqueness is per product, not global

A serial number is checked against the combination of serial number and product SKU, case-insensitively. The same serial number can therefore exist on two different SKUs without conflict. Do not rely on serial numbers being unique across your whole catalog.

Lot-tracked products

Lot numbers use the same pattern - an optional lot_number column on the receiving orders CSV. Lot tracking is more forgiving than serial tracking: a lot-tracked product with no lot_number produces a warning ("Lot number recommended"), not an error, so the row still imports.

Job management

Viewing upload history

The bulk upload page lists every upload job, with its ID and creation timestamp, entity type and file name, current status and progress, total records and success/failure counts, and an error download link for failed jobs.

Monitoring job progress

For large uploads:

  1. The job appears in the jobs list immediately after upload.
  2. Its status updates automatically as processing progresses.
  3. Select the job row to see detailed progress.
  4. Download the error report if any records failed.

Canceling jobs

  1. Find the job in the jobs list.
  2. Select Cancel (only available for pending or processing jobs).
  3. Confirm the cancellation.

The job status changes to Cancelled. Records already processed before you cancelled stay in the system.

Error handling

Common validation errors

Error Cause Solution
"Duplicate SKU" SKU already exists in system Use unique SKUs or update existing products
"Invalid barcode format" Barcode doesn't match standard format Verify barcode is 12-14 digit UPC/EAN
"Product not found" Referenced SKU doesn't exist Create product first or fix SKU reference
"Serial number conflict" Serial number already registered Use unique serial numbers
"Missing required field" Required column empty Fill in all required fields

Row-level errors

If individual rows fail during processing:

  1. The job finishes as Completed, even though some rows failed.
  2. An error report listing the failed rows is generated.
  3. Download the error CSV to identify the issues.
  4. Fix the errors and re-upload only the failed records.

Error report format

row_number,error_code,error_message,original_data
5,DUPLICATE_SKU,Product with SKU 'WIDGET-001' already exists,"WIDGET-001,Premium Widget,..."
12,INVALID_BARCODE,Barcode '123' is not a valid UPC/EAN format,"WIDGET-005,Another Widget,..."

Getting it right

Test with 10-20 records before uploading thousands, and validate the file in a spreadsheet first. Remove special characters and extra spaces, save it as UTF-8, and always start from the downloaded template rather than typing headers by hand.

Schedule large uploads during off-peak hours, keep each file at or under 2,000 records — see Row limit and skipped rows — and upload in logical batches, by storage class or supplier, rather than one giant file. Try a trial upload in a test warehouse before running it in production.

Make sure SKUs, barcodes, and serial numbers are genuinely unique, fill in optional fields where you have the data for better reporting, keep formatting consistent throughout the file, and confirm every referenced product or warehouse actually exists.

For serial numbers, expand quantities into individual rows — a serial-tracked line must have expected_quantity of 1 — and use a consistent, sequential numbering scheme. Uniqueness is checked per SKU, so a mislabeled serial won't be caught as a duplicate; make sure each one sits on the correct product_sku. A serial number already in the system fails the row, so re-upload only the units that haven't been received yet.

Troubleshooting

Problem What to check
File upload rejected immediately The file is CSV, not Excel .xlsx; it's under 10MB; it's UTF-8 encoded; the column headers match the template exactly; there are no hidden characters or byte-order marks (BOM)
Upload fails validation with errors Download the error report, compare your CSV against the template, and check for missing required columns, wrong data types, invalid dates (use YYYY-MM-DD), or duplicate values in unique fields
Job stuck in "Processing" Large uploads can take 10-15 minutes — refresh the browser to update the status. Check system performance during peak usage. Contact support if it's stuck over 30 minutes
Many rows failed during processing Download and review the error report, look for a common pattern, fix the source data, and re-upload the failed records
Fewer records imported than rows uploaded Check the job for a Skipped (row limit) count — files over 2,000 data rows import only the first 2,000. Download the skipped rows and upload them as a second file (see Row limit and skipped rows). If nothing was skipped, filter the results table by Validation Errors and Processing Errors. Confirm the file has no blank rows — they're filtered out before processing and never reach the record count
Duplicate serial number errors Check whether the serial number already exists for that SKU — the error names the existing record's status. Check the same serial number and SKU pair doesn't appear twice in your CSV — the error names the earlier row. Use serial number search to find the existing record, remove the already-received units, and re-upload the remainder

Support

For bulk upload assistance: