XCubes

Import a spreadsheet into a cube

You have a workbook and you want its numbers in a cube. XCubes reads the file server-side and writes the values, so the job is not transcription — it is telling it which part of the sheet is data, and which dimension items the labels correspond to.

That second half is where imports go wrong, and the failure is permanent: every distinct label in the range becomes a dimension item, and a dimension is shared across cubes, so a stray item appears in every cube that uses it. There is no undo. This guide is mostly about not doing that.

Which shape is your sheet?

Matrix — row labels down one column, column items across the header row. The normal layout of a financial model: line items × periods. Two dimensions, one per axis.

Jan 2026 Feb 2026 Mar 2026
Product sales 120 135 128
Service sales 40 44 51

Long — one row per record, one column per axis, and a single column of values. The normal shape of a system export.

Region Month Account Amount
UK 2026-01 Product sales 120
FR 2026-01 Product sales 84

A long sheet can carry any number of axes; a matrix import handles exactly two. If your workbook has several sections stacked on one tab — assumptions, then a P&L, then a debt schedule — that is several imports into several cubes, not one.

Import or Link?

An import is one-shot: it reads the file now and merges the values into the cube. A Link is a saved definition that can be re-run, refreshed on a schedule, previewed, and reverted, and it can translate codes through a mapping table.

Import when the file is a one-time load or a starting point. Build a Link when the source will be delivered again — a monthly export, an accounting extract — and especially when the file's codes differ from your item codes. The matrix wizard splits the difference: it can save its definition and re-run it against a new file, showing only the rows that changed.

Create the target first

Both imports write into a cube that already exists. Create the cube and its dimensions first, and if you already know the item codes, create the items too — an import into a populated dimension has something to match against, which is what turns "creates 47 items" into "matches 45, creates 2".

Preview before you write

In the UI: the matrix wizard walks you through it — upload, layout, axes, then a Match step listing every label with the item it resolved to, where anything unmatched must be pointed at an existing item or explicitly marked as a new one, and finally a Preview before the commit. The long-format importer has the equivalent in its column mapping and review steps.

Over MCP: nothing walks you through it, so the preview is a call you have to make:

preview_matrix_import({
  "attachmentId": "<file-id>",
  "cubeId": "<cube-id>",
  "rowDimensionId": "<rows>",
  "columnDimensionId": "<cols>",
  "startRow": 4,
  "endRow": 38
})

It writes nothing and takes the same parameters as the import. Read newRowItems and newColumnItems — the labels that would be created, by name — and sharedDimensions, the other cubes those items would show up in. A title row, a units row, a footnote, a source note, or a 2023 (p) provisional marker appears here as an item about to be minted. That is the moment to narrow the range, not after.

Against an existing cube, newRowItemCount: 0 and newColumnItemCount: 0 means every label matched. That is the signal to run for real.

Use the preview tool by name rather than a dry-run flag on the import: a client holding a stale schema silently drops an unknown parameter and the "preview" then imports for real, whereas an unknown tool name fails loudly.

Narrowing the range

Most sheets are not clean rectangles. Cut the range down rather than cleaning the file:

A report whose header row is one merged title is still importable: read the real label row, pass those names in columns[] by sheet position, and set startRow past them. Column numbers are bounded by the widest data row, not by the header.

Matching labels to existing items

Labels that differ from an existing item only mechanically — case, whitespace, or a short trailing unit tag, as in sheet Purchase Value against item Purchase Value (M) — reuse that item automatically, and the result lists them.

A label that differs in wording never merges by guess. Sheet Total revenues against item Revenue creates a second item unless you say otherwise, which you do with a rename map — in the wizard by picking the target on the Match step, over MCP with renameMap. The server will not infer a semantic rename, and that is deliberate: guessing one wrong silently merges two different measures.

Reconcile, then run

Before committing an import into a dimension that already has items, check both directions:

Either can be correct. What is not correct is claiming the cube will match the file without having looked. After the run, read the cube back and compare it against the source — the import moved values, but only a comparison shows that the formula items above them are wired to the right rows.

For a ledger export with its own chart of accounts, don't import it directly — see Build financial statements from a ledger export, which routes it through a mapping table and ties every line out first.