---
name: manifold-import
description: Import a merchant's data into Manifold, from a spreadsheet, another system's export, or what they tell you. Covers stock items, products, kits, prices, locations, suppliers and customers, purchase orders and receipts, sales orders, picks, shipments, refunds and stock adjustments. Recognise what the data is, shape it into rows, submit it safely with idempotent references, watch the job, and fix what was rejected. Use when someone wants to load, bulk-create, migrate or update records from a file or a list.
---

# Importing data into Manifold

You are bringing a merchant's records into Manifold in bulk. Every row you submit becomes an ordinary command to the part of Manifold that owns it, sent as the merchant you are connected as. So Manifold enforces its own rules on every row: a duplicate SKU, an oversell, a receipt against an unapproved order. What you can import is exactly what the merchant could do by hand.

The full reference, with every field, retry behaviour and limit, is `docs/import-export.md` in the Manifold repository and on the docs site. This skill is the working procedure.

## What you need

- **The Manifold connector** in this client. If it is not connected, follow the **manifold-onboarding** skill first.
- **The data.** A file the merchant shares with you, a table they paste, or a list they dictate. You cannot upload a file to Manifold over the connector. You read it and submit its rows. A merchant who would rather upload the file themselves does it in the admin at **Import & export** (§8).

## Never

- Never ask for, read out or type a password, an API key or any other credential, even if one appears in the merchant's file. Leave that column out and tell them it was there.
- Never invent data: no made-up SKUs, prices, costs, quantities, emails or ids. A blank you cannot fill stays blank, or you ask.
- Never apply anything the merchant has not seen. Stage, show, then commit (§5).
- Never re-submit a stock adjustment file. Adjustments are the one import that applies again when loaded twice (§7).
- Never split one document (a purchase order, a sales order, a refund, a product's variants, a kit's components) across two submissions (§4).

## 1. Reaching the tools

Every tool named here is served. `manifold_find_tools` searches them, `manifold_describe_tool` shows one's inputs, and `manifold_call_read` / `manifold_call_write` run one by name. The import tools:

| Tool | Does |
|---|---|
| `imports_list_targets` | Lists what can be imported and every field each target accepts. This is the live list; trust it over this page. |
| `imports_submit_rows` | Submits rows as JSON: the way you import. |
| `imports_preview_job` | Shows a job's staged rows, the target it is read as, and every target ranked by fit. |
| `imports_change_target` | Reads a staged job as another target. |
| `imports_map_columns` | Sets which column feeds which field. Only needed for a job the merchant uploaded. |
| `imports_commit_job` | Runs a staged job. |
| `imports_get_job` | A job's status and counters. |
| `imports_list_failures` | Every rejected row, with the field and the reason. |
| `imports_list_mappings`, `imports_save_mapping` | Saved column mappings, for files the merchant uploads again and again. |
| `imports_preview_export` | Reads a page of what an export would contain, filtered and narrowed to chosen columns. |
| `manifold_wait` | Holds one call open until a job finishes, instead of you polling. |

## 2. Work out what the data is

Read the first rows and the headers, and decide which **target** each sheet is. One sheet is one target; a workbook with tabs is several imports.

| The data holds… | Target | One row is… |
|---|---|---|
| SKUs, names, opening quantities (one location) | `inventory.item` | a SKU, once: a second row for it is refused |
| Products with sizes or colours over SKUs | `catalog.product` | a variant, grouped by `productKey` |
| Kits or bundles and their parts | `catalog.composite` | a component, grouped by the kit `sku` |
| Prices per price list | `catalog.price` | a SKU's price in one catalog |
| Warehouses, shops, 3PLs | `inventory.location` | a location |
| Suppliers or business customers | `crm.company` | a company |
| People: customers, contacts | `crm.person` | a person |
| Purchase orders | `purchasing.po` | a line, grouped by `reference` |
| Deliveries against approved POs | `purchasing.receipt` | a line, grouped by `receiptId` |
| Sales orders | `fulfillment.order` | a line, grouped by `reference` |
| Order history (shipped, cancelled, refunded) | `fulfillment.historical-order` | a line, grouped by `externalNumber` and `source` |
| Picked parcels | `fulfillment.pick` | a line, grouped by order + shipment |
| Courier manifests (tracking numbers) | `fulfillment.ship` | a parcel |
| Refunds | `fulfillment.refund` | a line, grouped by `reference` |
| Stock corrections | `inventory.adjustment` | a movement |

If you are unsure, submit with `run: false` (§5) and read `imports_preview_job`: its `candidates` rank every target by how well the columns fit, with `matched` (columns recognised) and `requiredMissing` (required fields with no column). Tell the merchant what you think the sheet is and why, and let them correct you.

## 3. Load in order

Each target needs what the ones before it create. For a full setup, go in this order and finish each before the next:

1. `inventory.location`: only the sites beyond `Main`, which exists already.
2. `inventory.item`: the SKU master, one row per SKU. Serial- or batch-tracked SKUs go in at quantity 0, and their stock arrives by receipt. A SKU held at several locations goes in once; its quantity per location is a count on the opening stocktake (`inventory_record_stocktake_counts`), which sets the figure and is safe to save again.
3. `catalog.product`, then `catalog.composite`.
4. `catalog.price`. The catalogs themselves are not importable: find them with `catalog_list_catalogs`, or create one with `catalog_create_catalog` after asking.
5. `crm.company`, `crm.person`.
6. `purchasing.po` creates **drafts**. A receipt needs an approved PO: the merchant approves in the admin, or you call `purchasing_authorise_po` with their go-ahead. Then `purchasing.receipt`, naming each PO by the `po_…` id from `purchasing_list_pos`.
7. `fulfillment.order`, then picks, shipments and refunds, naming orders, parcels and lines by the ids fulfillment issued (`fulfillment_get_order`). Orders long since finished are `fulfillment.historical-order` instead: kept for the record, holding no stock.

`inventory.adjustment` can run any time after the items exist.

## 4. Shape the rows

`imports_submit_rows` takes:

- `target`
- `headers`: the target's **field names**, exactly as `imports_list_targets` gives them (`unitCostCents`, not "Unit cost"). Only columns you have data for. Every required field must be there.
- `rows`: arrays of cells in header order: a string, a number, true/false, or null for blank.
- `reference`: your key for this batch (§5).
- `run`: false to stage for review, true to run at once.
- `filename`: optional, how the job is named in the admin.

Translate the merchant's data:

- **Money is whole cents**: 12.50 becomes `1250`. A cost or price column in dollars is converted, never copied.
- **SKUs** are uppercased by Manifold. Keep them exactly as the source spells them otherwise.
- **Yes/no** columns take true/false, yes/no, y/n or 1/0.
- **Dates** are ISO 8601 (`2026-11-30`).
- **Lists** (tags, roles, serial numbers) are one cell, separated by `;`.
- **Choices** must be one of the field's options (`imports_list_targets` lists them): `fifo`, `active`, `customer;supplier`. Map the source's words onto them, and ask when the mapping is not obvious.
- **Ids** of things already in Manifold are the ids Manifold shows (`loc_…`, `cat_…`, `prd_…`, `po_…`, `ord_…`, `shp_…`, `lin_…`), looked up with the list tools. The merchant's own document numbers go in `reference` columns.
- **Grouped targets**: a document's rows sit together, one after another. Its document-level columns (supplier, catalog, title, refund amount) are read from its first row. For these targets, keep each submission to **at most 500 rows**, whole documents only: rows are grouped 500 at a time, and a document cut across that boundary loses its later lines.

At most 5,000 rows per submission. Page larger sets into several submissions under distinct references, each holding whole documents only.

## 5. Stage, show, commit

1. **Mint a `reference` for the batch** before the first call, and reuse it on any retry: `<what>-<date>-<n>`, e.g. `items-2026-10-06-1`. A second submission with the same reference changes nothing and answers with the job already staged. A fresh reference on a retry is how data gets imported twice.
2. **Stage it**: `imports_submit_rows` with `run: false`. A 422 means a header is not a field, or a required field is missing. Fix the headers and resubmit under the **same** reference.
3. **Show the merchant**: `imports_preview_job` shows the first staged rows. Summarise what will happen ("312 SKUs, 40 with opening stock at Main; 3 rows have no name") and wait for their go-ahead.
4. **Run it**: `imports_commit_job` with the job id. It answers at once and the job runs in the background, one job at a time per merchant.
5. **Wait**: `manifold_wait` with `name: "imports_get_job"`, `arguments: { "jobId": … }`, `until: ["completed", "failed"]`. Call it again if it answers `reached: false`.

For a small, obvious import the merchant has already approved row by row, `run: true` stages and runs in one call.

Long imports: a job runs with the access the merchant had when it was committed, and that access lasts 15 minutes. Split very large loads into several jobs, each committed when the last has finished.

## 6. Read the result

`imports_get_job` reports `progress`: `applied`, `failed`, and `skipped` (the header row). `completed` means every row was tried. Some may still have failed. `failed` means the job itself stopped, and its `reason` says why.

For failures, `imports_list_failures` gives each rejected row's number, field and reason in plain words: "SKU TEE-S already exists", "only 3 on hand, 5 requested", "purchase order is draft, not authorised", "TEE-XL is not an inventory item — import it as an item first". Group them, explain them to the merchant, fix what can be fixed, and submit **only the corrected rows** under a **new** reference. Row numbers count from the header (row 0), so row 1 is the first data row.

## 7. What is safe to submit again

| Target | Loaded twice… |
|---|---|
| `inventory.item`, `catalog.product`, `catalog.composite`, `inventory.location` | refused as duplicates: safe |
| `catalog.price` | sets the same price: safe |
| `purchasing.po`, `fulfillment.order`, `fulfillment.refund` | recognised by their `reference`: safe |
| `fulfillment.ship`, `fulfillment.pick` | recognised by the parcel: safe |
| `fulfillment.historical-order` | recorded by content: the same file changes nothing, a corrected one replaces the order: safe |
| `crm.company`, `crm.person` | safe **only with a `reference` column**: give every row the source system's id. Without one, a company is created twice (a person is refused by email). |
| `purchasing.receipt` | safe **only with a `receiptId`** (`rcpt_` plus a uuid you mint per delivery). Without one, stock is received again. |
| `inventory.adjustment` | **applied again.** Never resubmit; correct with a new, opposite adjustment. |

## 8. When the merchant uploads the file themselves

Send them to **Import & export** in the admin (`https://admin.getmanifold.io/import`). They choose the file. Manifold recognises what it is from its columns and says so ("Recognised as Products and variants — 7 of 7 columns recognised"). They can switch it to another entity from the same picker, check each column's mapping, see any cell that would be rejected flagged in red, and start the import. You can follow along: their job appears in `imports_get_job` and `imports_list_failures` like any other.

## 9. Check the result

Read a sample back with the owning list tools (`inventory_list_items`, `catalog_list_products`, `crm_list_companies`, `crm_list_people`, `purchasing_list_pos`, `fulfillment_list_orders`), or page through what an export would hold with `imports_preview_export` (`columns` and `rules` narrow it, `offset` pages it). Then tell the merchant what is in, what was rejected and why, and what is left for them to do. Every change is on the activity trail as made by them.
