> ## Documentation Index
> Fetch the complete documentation index at: https://docs.realtimelca.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Add your materials to the inventory

> Use the MCP to import a QTO, bill of quantities, or similar file into your project inventory

You have a **quantity take-off (QTO)**, **bill of quantities**, or
similar file listing materials, quantities, units, and other data —
either structured (Excel, CSV, JSON) or unstructured (PDF, scanned
document, free-form text). Hand it to your AI client along with the
prompt below, and the agent will normalise it and populate your
Real-Time LCA project inventory in one pass.

## Prerequisites

* A QTO / BOM / similar file with materials and quantities
* An RTLCA project to import into
* An AI client connected to the [RTLCA MCP server](/ai-tools/rtlca-mcp)

<Card title="Download a sample BOQ" icon="download" href="/files/BOQ_Simplified_Building.xlsx">
  Don't have a BOQ to hand? Grab `BOQ_Simplified_Building.xlsx` and use it
  to walk through the prompt below.
</Card>

## Run the prompt

Open a chat in your connected AI client, attach (or paste) the QTO file,
and paste the prompt from the box below.

<Prompt description="Insert inventory materials from a BOQ or take-off" icon="boxes-stacked">
  ````text theme={null}
  ---
  name: rtlca-inventory-insert
  description: "Insert inventory materials (from a BOQ, take-off, or any quantity list) into an RTLCA building project via the Real-Time LCA MCP. Use this skill whenever the user wants to add materials, line items, or take-off entries to an RTLCA project — phrasings include 'add materials to my RTLCA project', 'upload the BOQ to RTLCA', 'put this take-off into the LCA project', 'create inventory entries in Real-Time LCA', 'insert into the inventory'. Also use it when the user asks how to structure a material entry, what units, tags, branch name, or typeIdentifier to use, or what fields rtlca_create_inventory_materials needs. Trigger even when only some materials are mentioned — the same field rules apply to a single entry as to a full BOQ."
  ---

  # Insert inventory materials into an RTLCA project

  The Real-Time LCA MCP exposes `rtlca_create_inventory_materials` for adding materials to a building project's inventory. The schema looks simple, but several fields are non-obvious — branch name must already exist on the project, declared units are enum integers (not strings or symbols), and a couple of fields the schema marks as optional are in fact required by the backend. This skill captures the exact recipe so the call succeeds on the first attempt.

  ## Workflow

  ### 1. Identify the target project

  If the user names a project, find its UUID:

  ```
  rtlca_list_projects()
  ```

  Match by name (case-insensitive substring is fine). Save the project's `id` — every subsequent call needs it as `projectId`.

  While you have the project record open, also note its `expectedLifespan` (or equivalent — usually 50 years for buildings). This becomes the default `lifetime` for inventory entries unless the user specifies otherwise.

  ### 2. Look up the branch name — DO NOT invent one

  This is the single most common failure mode. The `branchName` field on each entry must match a branch that already exists in the project (it's usually a Speckle model branch like `arkitektur`, `konstruktion`, `installation`). If you pass a name that doesn't exist (even something descriptive like the BOQ filename), the whole call fails with `branch with name X not found`.

  Get the existing branches:

  ```
  rtlca_get_material_mapping(projectId=<id>, byType=false)
  ```

  The response is keyed by branch name under `materials`:

  ```json
  { "materials": { "arkitektur": [ ... ] } }
  ```

  Use one of those keys as `branchName` for every entry. Ask the user to confirm the branch. If there is no branch, tell the user to create one.

  ### 3. Look up the enum values from the MCP

  Several fields on the entry are enum integers, not strings. The authoritative values live in the MCP and can change — don't hardcode them. Before constructing entries, fetch the enums you need:

  - **`declaredUnit`** (used inside `declaredUnitValuePairs`): call `rtlca_get_declared_units()`. Returns the symbol → enum mapping (kg, m, m², m³, pcs, etc.).
  - **Other enum fields if relevant**: call `rtlca_get_project_enums()` for the full set of enum domains used across project fields, or the more focused helpers like `rtlca_get_stage_types()`, `rtlca_get_industry_types()`, `rtlca_get_declared_mass_units()`, `rtlca_get_tags_types_forms()` depending on what you're filling in.

  Call these once per session — the values are stable within a session. Save the mapping locally and reuse it for every entry. Never guess an enum value from memory; if you can't recall having called the lookup tool in this session, call it now.

  ### 4. Read the source data

  If the materials come from an uploaded file (Excel BOQ, CSV take-off, etc.), read it first and lay out the structure: what's the material name, what's the quantity, what's the unit, what's the building element grouping. Don't write the API call until you've got the data laid out clearly — it's much easier to spot a unit mistake at this stage than after the call returns.

  ### 5. Map BOQ units to the declared-unit enum

  BOQs arrive in whatever units the QS chose. The job is to read the unit cell verbatim and map it to the enum returned in step 3 — not to decide what unit the material "should" be in.

  Common unit strings as written in real BOQs (these all need to resolve against `rtlca_get_declared_units()` output):

  | Written in BOQ                          | Maps to                  |
  |-----------------------------------------|--------------------------|
  | `m³`, `m3`, `cbm`, `cu.m`               | Cubic meters             |
  | `m²`, `m2`, `sqm`, `sq.m`               | Square meters            |
  | `m`, `lm`, `lin.m`, `rm` (running m)    | Meter                    |
  | `kg`                                    | Kilograms                |
  | `t`, `tonne`, `ton`                     | Tons                     |
  | `pcs`, `pc`, `nr`, `no.`, `each`, `stk` | Piece                    |

  Look up the enum integer for each name from the `rtlca_get_declared_units()` response. If a row has the unit blank or ambiguous, ask the user — don't guess. Guessing corrupts the data in a way that's hard to spot later.

  **Preserve the BOQ's native unit.** Don't pre-convert m³ of insulation into kg just because you think an EPD will be in kg. Storing materials in their natural take-off unit means you can pick EPDs that match the unit and avoid density-conversion errors at the mapping step. If the user explicitly asks for a different unit, do that — but flag what's being lost.

  If a row in the BOQ has multiple meaningful quantities (e.g. windows: both 12 pieces and 17 m²), pick the unit that's most useful for EPD mapping later. Set `materialCount` to the discrete count regardless.

  ### 6. Group entries with `typeIdentifier`

  `typeIdentifier` is the building element grouping in the LCA. Typical values:

  - `Substructure` — foundations, slabs, sub-base
  - `External walls`
  - `Internal walls`
  - `Roof`
  - `Openings` — windows, doors
  - `Finishes` — screeds, flooring, paint
  - `MEP` — pipes, cables, ducts

  These aren't a fixed enum on the API side, but the project's existing entries usually follow a convention. If `rtlca_get_material_mapping` from step 2 returned existing entries, match those values exactly (case- and whitespace-sensitive; "External walls" ≠ "external_walls" ≠ "Ext. Walls"). For a brand-new project with no prior entries, the categories above are a safe default.

  ### 7. Set `tags` and `speckleUserParameters` — required despite being marked optional

  The schema marks both fields as optional, but the backend rejects entries without them. Always include both:

  - `tags`: an array of strings — at minimum, mirror the `typeIdentifier` so the entry is filterable by element. Example: `["External walls"]`.
  - `speckleUserParameters`: a free-form object — useful for traceability. At minimum include the source (filename) and item number, e.g. `{"itemNo": "9", "source": "BOQ_Simplified_Building.xlsx"}`.

  If you skip either, the call fails (sometimes silently, sometimes with a 500).

  ### 8. Fill the remaining fields

  | Field                    | Type    | Notes                                                                              |
  |--------------------------|---------|------------------------------------------------------------------------------------|
  | `materialIdentifier`     | string  | The material's name — shows in the inventory UI                                    |
  | `description`            | string  | Free text — put the BOQ spec here (dimensions, density, grade)                     |
  | `branchName`             | string  | From step 2 — must exist                                                           |
  | `typeIdentifier`         | string  | From step 6 — building element                                                     |
  | `lifetime`               | integer | Years — default to the project's expected lifespan (often 50)                      |
  | `lifetimeOffset`         | integer | 0 unless the user specifies a delayed install/replacement                          |
  | `materialCount`          | integer | Number of discrete items (1 for continuous materials)                              |
  | `includeMapping`         | boolean | `true` so it counts in the LCA                                                     |
  | `isRecycled`             | boolean | `false` unless explicitly recycled content                                         |
  | `declaredUnitValuePairs` | array   | `[{ "declaredUnit": <enum from step 3>, "value": <num>, "preferred": true }]`      |
  | `tags`                   | array   | From step 7                                                                        |
  | `speckleUserParameters`  | object  | From step 7                                                                        |

  ### 9. Make the call

  Bundle all entries into a single `rtlca_create_inventory_materials` call — the tool accepts an array. One call for the whole BOQ is faster than separate calls and cleaner for the user to confirm.

  If the BOQ has materials in multiple branches, split by branch and call once per branch.

  ### 10. Confirm and next step

  After the call returns successfully, give the user a short summary:
  - How many entries were added
  - Grouped by `typeIdentifier`
  - Note that entries are unmapped (no EPD linked, so no GWP yet)
  - Offer the next step: automapping preview (`rtlca_preview_automapping`), manual EPD search (`rtlca_search_materials`), or stop here

  ## Example entry

  A single entry, fully populated, ready to go inside the `entries` array:

  ```json
  {
    "branchName": "arkitektur",
    "typeIdentifier": "External walls",
    "materialIdentifier": "Mineral wool insulation (ETICS)",
    "description": "ETICS facade, 160 mm × 100 m²",
    "lifetime": 50,
    "lifetimeOffset": 0,
    "materialCount": 1,
    "includeMapping": true,
    "isRecycled": false,
    "declaredUnitValuePairs": [
      { "declaredUnit": 5, "value": 16, "preferred": true }
    ],
    "tags": ["External walls"],
    "speckleUserParameters": {
      "itemNo": "9",
      "source": "BOQ_Simplified_Building.xlsx"
    }
  }
  ```

  The `5` for `declaredUnit` here corresponds to "Cubic meters" in the response from `rtlca_get_declared_units()` at the time this skill was written — but always confirm it against a fresh lookup rather than assuming it hasn't changed.

  ## Worked example

  User uploads a BOQ Excel with 30 materials and says "add these to my Sample Leuchtturm project".

  1. `rtlca_list_projects()` → finds "Sample Leuchtturm" with id `65ae34ee-...`
  2. `rtlca_get_material_mapping(projectId=..., byType=false)` → returns `{"materials": {"arkitektur": []}}` — branch is `arkitektur`
  3. `rtlca_get_declared_units()` → cache the symbol → enum mapping
  4. Read the Excel — confirm columns: item no, material, element, spec, quantity, unit
  5. For each row, resolve the unit string against the cached enum mapping
  6. Group by the BOQ's element column → use as `typeIdentifier`
  7. Build one entry per BOQ row, with `tags: [<element>]` and `speckleUserParameters: {"itemNo": ..., "source": "<filename>"}`
  8. Call `rtlca_create_inventory_materials(projectId=..., entries=[...30 entries...])`
  9. Reply: "Added 30 materials under `arkitektur` branch — 6 Substructure, 5 External walls, 3 Internal walls, 6 Roof, 3 Openings, 4 Finishes, 3 MEP. They're unmapped — run automapping or pick EPDs manually?"

  ## When the BOQ unit doesn't match available EPDs

  The mapping step can fail or get fuzzy when the inventory unit and the EPD's declared unit don't match. Two strategies:

  1. **Keep the inventory in the BOQ's unit and let the mapping step convert** using the EPD's reference density/area. Traceable to the source document, but introduces the EPD's density assumption.
  2. **Search for an EPD declared in the same unit as the inventory entry** (filter `rtlca_search_materials` results client-side by `declaredUnit`). Cleaner when such EPDs exist — for example, Boverket Klimatdatabasen has many m³-declared stone wool entries that map exactly to an m³ insulation entry without conversion.

  Option 2 is usually preferable for materials with both kg- and m³-declared EPDs in the library (insulation, masonry). Option 1 is fine when the BOQ unit is already what most EPDs use (concrete in m³, rebar in kg, doors in pcs).

  Tell the user which option you're taking and why.

  ## Common failures and fixes

  - **`branch with name X not found`** — you used a made-up branch name. Always fetch the real ones via `rtlca_get_material_mapping` first.
  - **Validation error on missing `tags` / `speckleUserParameters`** — the schema lies; both are required. Add them.
  - **`declaredUnit` validation error** — you passed `"m3"` or `"kg"` as a string, or guessed the integer. Look it up with `rtlca_get_declared_units()` and pass the integer the MCP returned.
  - **`typeIdentifier` doesn't appear in the dashboard grouping** — the value is case- and whitespace-sensitive. Match existing project values exactly.
  - **Materials added but excluded from LCA** — `includeMapping` was false, or the entry didn't get mapped to an EPD. Check with `rtlca_get_material_mapping(filter="unmapped")`.
  ````
</Prompt>

<Note>
  This prompt is also published as a **skill** so connected clients can
  install it once and reuse it without copy-pasting. See
  [Skills](/agent-ready/skills) for how to get it.
</Note>

## Next

Once the inventory is populated, [map materials to EPDs](/use-cases/map-materials-to-epds).
