Mappings
How a file's columns become product, variant and offer fields. FurniOps proposes a mapping for every new set of headers, a person confirms it once, and every later file with those headers imports on its own. Fields, settings, tiers and confirming through the API.
Every vendor names its columns its own way: Item #, Vendor SKU, W (in), Dealer Cost. A
mapping (map_…) says which canonical field each column fills and how its values are read.
- A mapping belongs to one source and one set of headers. A file whose headers change gets a new mapping; a source keeps one per header set it has seen.
- For a new header set FurniOps proposes a mapping, and the run stops as
needs_mappinguntil a person confirms it. That happens once: every later file with the same headers imports without asking. - Shopify feeds and Coaster use FurniOps' built-in mapping and never wait for one.
How FurniOps proposes
Each column is placed from the most specific evidence there is. The column's tier says which:
tier | Where it came from |
|---|---|
built_in | A platform's or connector's fixed mapping. |
learned | What a person chose for the same header on another of your sources. |
template | FurniOps' own template column names (sku, width_in). |
synonym | FurniOps' dictionary of the names vendors use. |
person | A person confirming this mapping. |
agent | An AI agent's proposal, applied without a person under auto_low_risk. Never learned for other sources. |
Units and currency are read from the header (W (in), Price (EUR)), and the decimal style from
the column's own values. Columns FurniOps could not place are listed in unmapped with the reason,
and are not imported unless you map them. With AI agents in your plan, the column repair
agent can propose fields for them.
Review and confirm
GET /v1/sources/{id}/mappings lists the source's
mappings, newest first; a waiting run waits for the one whose status is proposed. Each shows
the headers in file order, the first 5 rows as samples, the mapped columns and the unmapped
ones.
POST /v1/mappings/{id}/confirm confirms it. Send no
body to confirm the proposal as it is, or send the final columns: every column you want
imported, since columns left out are not.
// A run stopped as needs_mapping waits for the source's proposed mapping.
const mappings = await furniops.sources.listMappings("src_034XqBaL2mRLTZPXyRbaUc");
const proposed = mappings.data.find((m) => m.status === "proposed");
if (proposed !== undefined) {
console.log(proposed.columns, proposed.unmapped);
// Confirm it as proposed, or send the final columns: those left out are not imported.
const confirmed = await furniops.mappings.confirm(proposed.id, {
columns: [
...proposed.columns.map(({ column, prefix, field, settings }) => ({
column,
field,
settings,
...(prefix === undefined ? {} : { prefix }),
})),
{ column: "Seat Ht (in)", field: "seat_height", settings: { unit: "in" } },
],
});
console.log(confirmed.run?.id); // the waiting file resumes
}Confirming applies the mapping at once: the file waiting in needs_mapping resumes, or, for a
mapping confirmed again with changes, the source's current snapshot is
replayed. The answer holds the mapping and that run.
It answers 422 when a column is not one of the headers, a column is mapped twice, a field that
takes one column is given two, or a setting does not fit its field; and 409 while the source has
a run under way or is paused or disabled. Then nothing is confirmed.
A column
{
"column": "Seat Ht (in)",
"field": "seat_height",
"settings": { "unit": "in" },
"tier": "person"
}| Property | Meaning |
|---|---|
column | The header as the file writes it, or a JSON or XML record's flattened path. |
prefix | true: the mapping covers every column that starts with column (options. for options.Size, options.Colour). |
field | The canonical field it fills (below). |
settings | How its values are read (below). |
tier | Where the mapping came from (above). Not sent when you confirm. |
Settings
| Setting | For | Meaning |
|---|---|---|
unit | Lengths, dimensions, rug sizes, weights, cartons | The column's unit: mm, cm, m, in or ft for lengths and cartons' sizes; g, kg, oz or lb for weights. |
style | Numbers and money | The decimal separator: dot (1,299.00) or comma (1.299,00). |
currency | Money | The column's currency, when it differs from the source's. |
separator | Lists, cartons | What separates a list's items (or cartons) in one cell; a vertical bar when unset. Never ; for cartons. |
axis | axis | The variant axis the column names (finish, size). |
html | Text | The column holds HTML: it is read as plain text. |
Fields
Every field a column can fill, with the level it belongs to and the kind of value it holds. A repeatable field takes several columns: each adds to the list, or names its own axis.
| Field | Level | Value | Columns |
|---|---|---|---|
| group | Product | text | One |
| title | Product | text | One |
| brand | Product | text | One |
| collection | Product | text | One |
| description | Product | text | One |
| category | Product | list | Several |
| features | Product | list | Several |
| tags | Product | list | Several |
| country_of_origin | Product | country | One |
| tsca_title_vi | Product | boolean | One |
| ca_tb117_2013 | Product | boolean | One |
| prop65_warning | Product | boolean | One |
| product_images | Product | url_list | Several |
| variant_key | Variant | text | One |
| sku | Variant | text | One |
| gtin | Variant | gtin | One |
| mpn | Variant | text | One |
| variant_title | Variant | text | One |
| axis | Variant | text | Several |
| colour | Variant | text | One |
| finish | Variant | text | One |
| materials | Variant | list | Several |
| dimensions | Variant | dimensions | One |
| width | Variant | length | One |
| depth | Variant | length | One |
| height | Variant | length | One |
| diameter | Variant | length | One |
| seat_height | Variant | length | One |
| seat_depth | Variant | length | One |
| arm_height | Variant | length | One |
| weight | Variant | weight | One |
| assembly_required | Variant | boolean | One |
| white_glove | Variant | boolean | One |
| freight_class | Variant | text | One |
| rug_size | Variant | rug_size | One |
| discontinued | Variant | boolean | One |
| replacement_sku | Variant | text | One |
| images | Variant | url_list | Several |
| cartons | Variant | cartons | Several |
| location | Offer | text | One |
| cost | Offer | money | One |
| msrp | Offer | money | One |
| map | Offer | money | One |
| price | Offer | money | One |
| compare_at | Offer | money | One |
| quantity | Offer | integer | One |
| incoming | Offer | integer | One |
| availability | Offer | availability | One |
| lead_time_days | Offer | integer | One |
A variant's shipping boxes go in cartons: several boxes in one cell, each with its size, weight and
count. Cartons describes how to write them.
When a cell cannot be read
A cell FurniOps cannot use does not stop the run: the rest of its row is imported, and the run records the cell as a problem, with its line, column, field and why.
Files
Price lists and catalogues as files. Fetch a file from a URL, upload one through a presigned URL, or let vendors email files to the source's own address, with sender checks and an inbox log.
Cartons
How to write a variant's shipping boxes in one spreadsheet cell, which column settings apply, and how the cartons appear on the product. Sizes, weights and counts, several boxes per cell and several columns per file.