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

# 2026

<Update label="June 30, 2026" description="v3.585.1">
  ## Verify locked versions with the Verifier role

  Teams can now sign off on locked versions. A new **Verifier** role grants
  read-only access plus one capability — marking a locked version as
  **Verified**, the highest-confidence state, recorded in the audit history with
  an optional note. Assign the role from Users & roles, then use **Mark as
  verified** on a locked version. See
  [Product and material versioning](/docs/help/versioning).
  <Badge color="yellow" size="sm">Feature flag</Badge>
</Update>

<Update label="June 27, 2026" description="v3.581.0">
  ## Set a dataset on an element through the API and MCP

  You can now bind an emission-factor dataset to a sourced element — material,
  energy, or process — in the same call that creates or updates it, instead of
  only reading the binding back. Send `dataset: { uuid }` (plus an optional
  `datasetAmount`) to point the element at a dataset; binding one makes the
  element external (`dataSource: external`), and sending a different dataset on
  update re-points it. Use `/v1/database` to find a dataset to bind.

  These writes are available through MCP as well, alongside the rest of the
  element write contract: `weight`, `packagingWeight`, `recycledPercent`,
  `dataSource`, `supplierPartId`, `cpcCode`, and `dataQualityIndicators`. Custom
  transport modes accept their backing dataset as `dataset` too (the older
  `footprint` field still works but is deprecated). See [Datasets](/docs/dataset)
  and the [Material](/api-reference/v1/material/post) reference.
</Update>

<Update label="June 25, 2026" description="v3.572.0">
  ## Justify your data quality ratings

  You can now record a free-text justification alongside your data quality
  ratings — why those ratings were chosen, which production years and background
  datasets the figures rest on, and any caveats a reviewer should know. It reads
  and writes through the API and MCP as `dataQualityIndicators.description`, on
  both products and materials, and is editable in the Variable app next to the
  ratings it explains. See [Data quality](/docs/help/data-quality).
</Update>

<Update label="June 25, 2026" description="v3.569.1">
  ## Set data quality indicators through MCP

  Data quality indicators are now available through MCP, matching what you can
  already do in the Variable app. When you create or update a product or
  material, you can record the documentation year, the share of the footprint
  backed by primary data, the share backed by supplier-specific data, and the
  five data quality ratings (technological, temporal, geographical,
  completeness, and reliability). The average data quality score is still
  calculated for you. See [Data quality](/docs/help/data-quality).
</Update>

<Update label="June 25, 2026" description="v3.569.0">
  ## Bundle or unbundle optional benefits (D)

  Bill-of-materials lines placed at **optional benefits (D)** can now toggle
  between bundled and unbundled, the same as every other stage group. Bundled
  (the default) imports the source's cradle-to-gate (A1–A3) total stamped onto
  module D; unbundled imports the source's own module-D value — its recycling or
  recovery credit — into stage D. Existing inputs are unchanged: the default
  stays bundled. See [Life-cycle stages explained](/docs/help/life-cycle-stages).
</Update>

<Update label="June 24, 2026" description="v3.568.0">
  ## Richer dataset reference on custom transport modes

  A custom transport mode is backed by a single freight emission-factor dataset,
  and its `dataset` reference now carries the same detail as external elements:
  alongside `uuid` and `name`, read responses include the `source` database and
  the `declaredUnit` (a freight unit — tonne-kilometre or tonne-mile). Built-in modes have no backing dataset
  and omit it. There's no `datasetAmount` — a mode's factor is a unit quantity in
  its declared unit, so it adds nothing beyond `declaredUnit`.

  The enriched reference also surfaces through the MCP `get_transport_mode` and
  `list_transport_modes` tools. See the
  [Transport mode](/api-reference/v1/transport/mode/list/get) reference.
</Update>

<Update label="June 24, 2026" description="v3.567.1">
  ## See the dataset behind an external element

  External elements — materials, energy, and processes that get their impact from
  a single emission-factor dataset (`dataSource: external`) — now tell you which
  dataset that is. Read responses include `dataset` (the backing dataset's `uuid`,
  `name`, `source` database, and `declaredUnit`) and `datasetAmount` (the quantity
  and unit consumed). Both are absent for modeled products. If you narrow a
  response with `return`, list `dataset` / `datasetAmount` there too or they're
  omitted.

  The same fields surface through the MCP element tools (`get_material`,
  `list_materials`, and the energy/process element tools). See the
  [Material](/api-reference/v1/material/get),
  [Energy](/api-reference/v1/energy/get), and
  [Process](/api-reference/v1/process/get) references.
</Update>

<Update label="June 24, 2026" description="v3.566.0">
  ## Absolute impact contributions in the inventory tab

  In the app, a product's **Inventory** tab now shows each element's **absolute**
  contribution to the model — its intensity scaled by quantity into the model's
  stages — instead of a per-unit intensity, so you can see what every material,
  process, and transport segment actually adds to the product's footprint. The
  GWP-fossil contribution shows for everyone; a missing value stays a dash and a
  real zero shows zero.

  With all impact indicators enabled, a selector above the table swaps the impact
  column across every EN 15804+A2 indicator the model exposes — each in its own
  unit — defaulting to GWP-fossil. See
  [Environmental impact indicators](/docs/help/impact-indicators). <Badge color="yellow" size="sm">Preview</Badge>
</Update>

<Update label="June 24, 2026" description="v3.563.0">
  ## Attach transport to a model input

  You can now attach a freight
  [transport lane](/api-reference/v1/transport/lane/list/get) to a model input
  through the public API and MCP — modeling the transport leg of a product's bill
  of materials end-to-end without dropping into the app. Send a `transport` block
  on `POST` / `PATCH` (and the bulk input endpoint) with the `lane` to follow and
  either the material input it carries (`for`) or an explicit cargo `weight`; send
  `transport: null` to detach.

  When you set `for`, the leg takes its lifecycle stage and cargo weight from the
  carried material, so `weight` is derived rather than sent. Reads echo the lane,
  weight, carried material, and the derived tonne-kilometres (`tkm`); the leg's
  carbon footprint flows into the model total. The MCP `create_model_input` and
  `update_model_input` tools gain the same field. See
  [Models & Inputs](/api-reference/v1/model/inputs/post).
</Update>

<Update label="June 22, 2026" description="v3.549.0">
  ## Deleting an import undoes the field updates it made

  Deleting a product or material import no longer just removes the items it
  created — it also **reverts the scalar updates** that import made to
  pre-existing items. Fields it overwrote are restored to their prior values, and
  blanks it filled in are cleared again. The revert is guarded: any field you (or
  a later import) changed after the import is left untouched, so a delete never
  clobbers newer edits. When some fields are skipped for that reason, the app
  tells you how many couldn't be reverted. See
  [Undoing an update](/docs/import/import-behavior#undoing-an-update).
</Update>

<Update label="June 19, 2026" description="v3.539.0">
  ## Material imports default to filling in blanks

  Material imports now default to **Fill missing only**, so re-importing a file
  backfills blank fields — weights, CPC/HS codes, descriptions, notes — on
  materials you already have, without overwriting anything you've already set. A
  stored `0` or empty value still counts as set and is kept. Product and other
  import types are unchanged and still default to **Create new only**; you can
  switch either to any mode on the upload screen. See
  [Import match mode](/docs/import/import-behavior#import-match-mode-products-and-materials).

  You can also see which existing products an import updated, not just the ones it
  created: an import's detail view now lists updated products under their own
  **Updated** tab.
</Update>

<Update label="June 19, 2026" description="v3.535.0">
  ## Energy and process elements in the public API

  You can now manage energy and process elements through the public API and MCP,
  the same way you already manage materials: list them, create and update them,
  upload an image, and delete them — one at a time or in bulk. New `/v1/energy`
  and `/v1/process` endpoints mirror `/v1/material`.

  An energy or process element behaves like any other element; what makes it
  "energy" or "process" is simply its place in the taxonomy. Creating one files it
  under the matching category automatically, and you can pass a `taxonomy` to pick
  a more specific subcategory.

  `GET /v1/material` now returns materials (and any uncategorized elements), so
  energy and process elements no longer appear there — reach them through their own
  endpoints. See the [Energy](/api-reference/v1/energy/list/get) and
  [Process](/api-reference/v1/process/list/get) references.
</Update>

<Update label="June 17, 2026" description="v3.526.0">
  ## Update existing materials and products on import

  Material and product imports now have a **match mode** that decides what happens
  when a row matches an item you already have. Pick it on the upload screen:
  **Create new only** (the default — matched rows are skipped, today's behavior),
  **Overwrite existing** (the imported value wins), or **Fill missing only** (fill
  blanks, keep what you've already set). Use it to backfill enriched source data —
  weights, CPC/HS codes, descriptions, notes — onto items you created earlier,
  without re-keying them by hand.

  Only the item's own fields are updated; its relationships — supplier, assigned
  datasets, and bill-of-materials inputs — are left untouched in every mode. Under
  **Fill missing only**, a stored `0` or empty value counts as set and is kept:
  Variable treats zero as a value and only a truly missing field as missing.
  Re-imports still match by SKU (or name), so they never create duplicates. See
  [Import match mode](/docs/import/import-behavior#import-match-mode-products-and-materials).
</Update>

<Update label="June 16, 2026" description="v3.518.0">
  ## Set data quality indicators on materials

  Material elements now have an editable data quality panel beside the dataset
  passport, so you can set all five pedigree-matrix dimensions — technological,
  temporal, and geographical representativeness, completeness, and reliability —
  directly on a material, not only on the dataset it points to. The aggregate
  data quality rating folds in all five and updates as you edit.
</Update>

<Update label="June 16, 2026" description="v3.512.0">
  ## Public API: set temporal representativeness directly

  `temporalDQR` is now a writable data-quality indicator on product create and
  update, alongside the other pedigree-matrix ratings (`technologicalDQR`,
  `geographicalDQR`, `completenessDQR`, `reliabilityDQR`). Send `1` (Good — same
  reporting year), `2` (Fair — less than 5 years old), or `3` (Poor — more than
  5 years old).

  Previously `temporalDQR` was read-only and inferred from `documentationYear`;
  it's now set directly, matching the data-quality panel in the app.
  `documentationYear` remains a separate, writable field for reference.
</Update>

<Update label="June 16, 2026" description="v3.508.0">
  ## Public API & MCP: recycled content on products

  Product and material responses now carry recycled-content percentages.
  `recycledPercent` is the value you declare (0–100) — a `0` is a declared zero,
  while an undeclared value is absent — and `modeledRecycledPercent` is the
  server-computed weight-weighted average of the contributing inputs' recycled
  content, the recycled-content counterpart to `modeledWeight`.

  Each model input also carries its own `recycledPercent`, inherited from the
  input's source product — the source's modeled recycled content when available,
  otherwise its declared value, or `0` when there is no source product or the source
  has neither — so you can trace a model's recycled content line by line.
  `modeledRecycledPercent` is derived from the bill of materials, read-only
  (ignored if sent on create or update), and present only once a model has
  contributing inputs. The same fields flow through every MCP tool that reads a
  product or material.
</Update>

<Update label="June 15, 2026" description="v3.503.0">
  ## Public API & MCP: total modeled weight on products

  Product and material responses now also carry `modeledTotalWeight` — the gross
  modeled weight, the sum of `modeledWeight` and `modeledPackagingWeight` — in the
  same `Amount` shape (`{ quantity, unit }`).

  It's derived at read time from the two component weights rather than stored, so
  it can't drift from them: a missing addend contributes nothing, the unit follows
  the first present addend, and the field is present whenever at least one of the
  two is. Like its parts it's read-only (ignored if sent on create or update). The
  same field flows through every MCP tool that reads a product or material.
</Update>

<Update label="June 12, 2026" description="v3.491.0">
  ## Filter the product inventory by dataset status

  The product inventory now has a **Dataset status** filter so you can narrow the
  list to items that already have a dataset assigned (**Assigned**) or still need
  one (**Unassigned**). Use it to find the products missing footprint data before
  a bulk update. The filter applies to assignable items (products and energy);
  composed (live) items count as assigned.
</Update>

<Update label="June 11, 2026" description="v3.478.0">
  ## Public API & MCP: modeled packaging weight on products

  Product and material responses now also carry `modeledPackagingWeight` — the
  server-computed sum of a model's A1–A3 packaging input weights — alongside the
  declared `packagingWeight`, in the same `Amount` shape (`{ quantity, unit }`).

  It's the packaging counterpart to `modeledWeight`: compare it against the
  `packagingWeight` you declared to mass-balance packaging the way an EPD reviewer
  would. It's derived from the bill of materials, read-only (ignored if sent on
  create or update), and present only once a model has qualifying packaging inputs.
  The same field flows through every MCP tool that reads a product or material.
</Update>

<Update label="June 10, 2026" description="v3.466.0">
  ## Public API & MCP: modeled weight on products

  Product and material responses now carry `modeledWeight` — the server-computed
  sum of a model's A1–A3 product-group material input weights (excluding packaging
  and ancillary inputs) — alongside the existing `weight` field, in the same
  `Amount` shape (`{ quantity, unit }`).

  Use it to compare what a model actually adds up to against the weight you
  declared: `weight` is the value you set, while `modeledWeight` is derived from
  the bill of materials and is read-only (ignored if sent on create or update).
  It's present only once a model has qualifying inputs. The same field flows
  through every MCP tool that reads a product or material.
</Update>

<Update label="June 10, 2026" description="v3.459.0">
  ## Auto-assign transport across a bill of materials

  Variable can now fill in transport for you. Instead of hand-attaching a lane to
  every material in a model, use **Auto-assign transport** on a product — or select
  several products on the product list and run it in bulk. For each material,
  Variable reads the facility it's made at (or its supplier's location) and the
  facility your product is made at, finds an existing transport
  lane that connects those points within a radius you choose, and creates the
  transport input using the material's own weight.

  The closest matching lane wins; a tie with no clear winner is left for you to
  resolve, and transport you've already set is never overwritten. When the run
  finishes you get a summary grouped by result — assigned, no match, ambiguous, and
  the rest. The default radius is 50 km, adjustable per run. <Badge color="yellow" size="sm">Preview</Badge>

  ## Public API & MCP: categorize model inputs

  You can now read and write an input's **material group** through the
  [API](/api-reference/v1/model/input/post) and MCP. Each input carries a
  `materialGroup` array — common values are `product`, `packaging`, and
  `ancillary`, and custom values (for example from a PCR document) are allowed.

  Set it on create or update and it comes back on every read (single input and
  list). This lets integrations model packaging and ancillary inputs end-to-end
  instead of relying on the value Variable infers. The legacy `isPackaging` flag
  stays consistent automatically.
</Update>

<Update label="June 7, 2026" description="v3.452.0">
  ## Public API & MCP: write a dataset's impact indicators

  You can now set a dataset's environmental impact indicators through the
  [API](/api-reference/v1/dataset/impacts/put) and MCP — the write counterpart to
  reading them. `PUT /v1/dataset/{uuid}/impacts` takes the same keyed shape the
  read endpoints return: each EN 15804+A2 indicator code (e.g. `GWP-fossil`,
  `ODP`) maps to its life-cycle stage values (`A1`–`A5`, `B1`–`B7`, `C1`–`C4`,
  `D`). Totals like `A1_A3` and `totalCarbonFootprint` are computed for you, and
  the dataset's footprint is recalculated on write.

  Indicators you omit are left unchanged, so you can fill in a dataset
  incrementally. The dataset must be in draft state (newly created datasets are).

  Using an MCP client, this means an assistant can now build a fully populated
  dataset from a source document in one flow — create the dataset, then call
  `set_dataset_impacts` to write its indicator values.

  ## Public API & MCP: all impact indicators in responses

  Products, materials, datasets, switches, and [database search](/api-reference/v1/search/list)
  results can now return their full set of [EN 15804+A2 impact indicators](/docs/help/impact-indicators)
  — acidification, ozone depletion, water and energy use, waste, and the rest — not just carbon.
  Impacts come back under a new top-level `impacts` field, keyed by indicator code (for example
  `GWP-fossil`, `ODP`, `AP`).

  Opt in with the `impacts` query parameter: `?impacts=all` for everything, or
  `?impacts=gwp-fossil,odp,ap` to filter to a subset. Without it, responses return `GWP-fossil`
  only, so existing integrations are unaffected. The same data flows through every MCP tool that
  reads these resources.

  The existing `footprint.CO2e` field is unchanged and still works, but is now **deprecated** in
  favor of `impacts["GWP-fossil"]`. See [Impacts](/docs/impacts) for the response shape and query
  semantics.
</Update>

<Update label="June 5, 2026" description="v3.443.0">
  ## Public API & MCP: manage your locations

  You can now manage your company's locations — factories, warehouses, offices,
  and ports — through the [API](/api-reference/v1/location/list) and MCP, not just
  in the app. List the sites you already have, add new ones, update their details,
  and remove the ones you no longer need. Pin a site to a point on the map so it
  carries real coordinates, or keep it as a plain address. You can also set up a
  location on behalf of a supplier you work with, not only your own company.

  Once a location exists, reuse it anywhere you pick a place — for example the
  start or end of a [transport route](/api-reference/v1/transport/lane/post). A
  location that's still in use is protected: remove the places that reference it
  before you can delete it.

  Using an MCP client, just ask your assistant to list, create, update, or delete
  a location.
</Update>

<Update label="June 4, 2026" description="v3.431.0">
  ## Public API & MCP: fetch a supplier by internalId

  `GET /v1/supplier/{id}` now resolves a supplier by its `uuid`, `syncId`, or
  `internalId` — the same `internalId` the API returns on the supplier — so you
  can read a supplier back by the identifier you already have. The MCP
  `get_supplier` tool accepts all three. `PATCH` and `DELETE` continue to require
  the `uuid`.
</Update>

<Update label="June 4, 2026" description="v3.429.2">
  ## Public API & MCP: model freight transport

  Build and reuse freight routes through the API. **Transport lanes**
  (`/v1/transport/lane`) are ordered legs, each with a mode, an origin, and a
  destination; distances auto-compute from the endpoints, and a lane reports its
  carbon intensity (`CO2ePerTkm`) rather than an absolute total — the total
  materialises once cargo weight is applied. **Transport modes**
  (`/v1/transport/mode`) cover the system-curated built-ins (`truck`,
  `container-ship`, `train`, the `air-*` haul bands, …) plus custom modes you
  create from your own freight emission-factor datasets. Lanes support full
  CRUD; built-in modes are read-only, while the custom modes you create are
  fully editable.

  The MCP server exposes the same operations as transport-lane and
  transport-mode tools, so an agent can assemble routes end to end.
</Update>

<Update label="June 4, 2026" description="v3.428.0">
  ## Public API: read and write product notes

  Products now expose their `notes` field through the API and MCP. `GET`
  responses include `notes`, and you can set it on `POST`/`PATCH` (or via the
  `create_product`/`update_product` MCP tools) alongside `description`.
</Update>

<Update label="June 4, 2026" description="v3.423.0">
  ## Public API: resolve addresses into transport-leg locations

  Two new endpoints turn a free-text address into a referenceable location, so you
  can set transport-leg origins and destinations programmatically instead of
  pre-seeding them in the app. `GET /v1/geolocation?search=...` geocodes a string
  like `Syracuse, NY` into ranked, un-persisted candidates; `POST /v1/geolocation`
  persists a chosen candidate (by `googleMapsId`) or a manual point
  (`name` + coordinates) and returns it with a `uuid`. Reference that `uuid` — or a
  candidate's `googleMapsId` directly — on a transport leg.

  The MCP server exposes the same surface as `geocode_location` and
  `create_geolocation` tools.
</Update>

<Update label="June 3, 2026" description="v3.413.1">
  ## Terrestrial eutrophication now reported in mol N eq

  The EP-terrestrial (Terrestrial eutrophication) impact indicator is now
  reported in `mol N eq`, matching EN 15804+A2. It was previously labelled
  `mol H+ eq` (the acidification unit) — a mislabelling inherited from
  ecoinvent, which documents the same error in its
  [v3.11 known issues](https://support.ecoinvent.org/ecoinvent-version-3.11).
  The stored values are unchanged — only the unit label was corrected.
</Update>

<Update label="May 29, 2026" description="v3.390.0">
  ## Inputs editor: enter quantities in alternative units

  The input amount field now groups units by dimension and lets you enter
  a quantity in any unit the source product supports — e.g. enter a board
  as `2.4 m` against an `m²`-declared product, and Variable converts to the
  declared unit using the product's conversion factors. The source
  product's length, volume, and area conversion factors are now editable
  directly from the product preview sidebar. This is the UI counterpart to
  the `declaredAmount` API shape shipped on May 22.

  ## Location picker: pick a location without an address

  Picking a `Location` that has no attached `GeoLocation` in the
  transport-leg location picker now works — previously the click was
  silently dropped, blocking address-less custom locations from being used
  as a segment endpoint.

  ## Public API: machine-readable taxonomy path on responses

  Taxonomy references on `/v1/product`, `/v1/material`, and `/v1/activity`
  responses now include a `path` field (e.g. `material`, `energy/electricity`)
  alongside `uuid` and `name`. The path is a stable, machine-readable grouping key
  — unlike the human-readable `name` — so you can reliably branch or aggregate on a
  category. Purely additive; existing `uuid`/`name` readers are unaffected.

  ## File import: BOM rows in mixed units

  BOM imports now accept an input quantity in any unit the source product supports,
  even when it differs from that product's declared unit (e.g. listing a board as
  `2.4 m` against an `m2`-declared product). Variable converts the value to the
  declared unit for impact calculations using the product's conversion factors and
  keeps your entered value in the API. Rows whose unit has no matching conversion
  factor are skipped. See the
  [BOM preparation guide](/docs/import/prepare-bom) for details.

  ## Suppliers can have their own locations

  A supplier can now own one or more named locations — a factory, warehouse, or
  port, each with its own address — managed from the new **Locations** tab on the
  supplier page. Mark one as the default (the first one added is set automatically),
  and it's used wherever a supplier needs a single address. Supplier locations also
  appear anywhere you pick a place, such as the origin of a transport leg, searchable
  by supplier name, so you can reuse *"Acme — Rotterdam warehouse"* instead of
  re-typing the address.

  ## Public API: fetch a single model input

  New `GET /v1/model/{modelId}/input/{inputId}` returns one input on a given model.
  Responds with `404` when the input does not belong to the model (or does not
  exist for this caller). The response carries the same `quantity` / `unit` /
  `declaredAmount` shape as the list endpoint.

  The MCP server exposes the new endpoint as a `get_model_input` tool, alongside
  refreshed `list_model_inputs`, `create_model_input`, and `update_model_input`
  descriptions that surface the alternative-unit semantics introduced last week.

  ## Owner-controlled AI features toggle

  Company owners can now enable or disable AI features for their company directly
  from **Company Settings** with the new "Enable AI features" switch. Previously
  this required Variable support to flip the company-level `DisableAI` flag.
</Update>

<Update label="May 25, 2026" description="v3.353.0">
  ## Activities anchored in your company timezone

  Activity dates now honor a `Company.timezone` (IANA, defaulting to
  `Europe/Oslo`), so partial inputs like `"2026"` or `"2026-03"` resolve in the
  company's timezone instead of UTC. Period filters (YTD, MTD, specific years)
  bucket from the same timezone, and each `Activity` exposes a new `timezone`
  field recording the zone used on write.
</Update>

<Update label="May 22, 2026" description="v3.335.0">
  ## Public API: alternative-unit response shape on `/v1/model/{id}/input`

  GET, POST, and PATCH responses on `/v1/model/{id}/input` now return the `quantity`
  and `unit` you supplied, even when those units are in a different dimension than the
  source product's declared unit (e.g. sending `kg` for an `m3`-declared source product
  with a weight conversion factor). A new read-only `declaredAmount` field carries the
  canonical converted amount in the source product's declared unit — this is what
  impact calculation uses. `declaredAmount` is present only for cross-dimensional
  inputs; same-dimension entries (e.g. `l` against an `m3`-declared product) are
  unchanged. `declaredAmount` is ignored if supplied on POST/PATCH.

  ## Bulk delete activities from the activity list

  Activities can now be deleted in bulk directly from the Activity list. Select activities on the
  Draft or Rejected tab and use the new Delete button in the floating bulk action bar.

  ## Admin-managed maintenance windows for self-hosted

  Self-hosted operators can now schedule and run maintenance windows from
  **Admin -> Maintenance windows** instead of toggling an environment variable
  and redeploying. Configure a recurring window (daily, weekly, or monthly in
  any IANA timezone) with an `HH:mm` start and a duration up to seven days, or
  hit **Start now** for a one-off window.
</Update>

<Update label="May 21, 2026" description="v3.333.0">
  ## Offline ISO-3166-1 country coverage for self-hosted

  The seeded `GeoLocation` dataset now covers the full ISO-3166-1 country and territory set (plus
  Kosovo, which Google Maps treats as `XK`). Self-hosted instances can resolve any country offline
  without a Google Maps API key. Each row ships with the same `googleMapsId`, lat/lng, and bounds
  that Google's Geocoding API returns, so locations created via seed match locations created via
  online lookup.
</Update>

<Update label="May 19, 2026" description="v3.322.1">
  ## BOM import: supplier matching and XLSX precision fixes

  BOM imports now match existing suppliers by `supplierId` (internalId) alone when no
  `supplierName` is provided, so id-only rows correctly link to suppliers already in your
  account instead of falling through and creating products without a supplier link. The
  supplier lookup also no longer caps at the first page, so accounts with more than 250
  suppliers match reliably.

  XLSX imports now preserve full numeric precision (previously, cells like `0.0004`
  displayed as `0.000` were silently truncated to the display string), normalize date
  cells to ISO strings, and drop spreadsheet error sentinels (`#REF!`, `#N/A`, `#VALUE!`,
  etc.) instead of importing them as literal strings.
</Update>

<Update label="May 1, 2026" description="v3.280.1">
  ## Bulk location import

  Users can now bulk-create or update Locations from CSV or XLSX files. The Locations page
  exposes an Import action that maps name, address, and type columns; re-imports merge on
  name within the company so existing values are preserved when a column is left blank.
  Addresses are geocoded in the background after import.
</Update>

<Update label="April 21, 2026" description="v3.266.0">
  ## SmartEPD integration

  Companies can now connect Variable to SmartEPD v2 and push EPDs directly from the platform.
  Owners configure the connection in Company Settings → Integrations by providing their SmartEPD
  API key and selecting a workspace. On each EPD, a "Sync to SmartEPD" action lets users choose a
  PCC and project (creating a new project if needed) and submit the EPD to SmartEPD. A sync status
  badge on the EPD page shows the current state (pending, syncing, synced, failed, or out of date),
  and a "Check status" action polls SmartEPD for the latest review state. Once an EPD is approved
  or published in SmartEPD, Variable automatically locks the EPD to Verified or Published to mirror
  the remote lock. If the EPD is edited locally after its last successful sync, the auto-lock is
  skipped and the record is marked `out_of_date` instead.
</Update>

<Update label="April 18, 2026" description="v3.260.0">
  ## Dataset resource in the public API

  The public API now supports managing datasets in your account via `/v1/dataset`. You can list, fetch, create,
  update, upload images, and delete datasets - whether they were user-created, added from the Variable Database,
  or shared by a supplier. Deleting a dataset that came from the Variable Database or a supplier removes it from
  your account while leaving the original in place; user-created datasets are permanently deleted.

  New `DATASET_READ`, `DATASET_CREATE`, `DATASET_UPDATE`, and `DATASET_DELETE` permissions gate these routes.
  The same operations are exposed as MCP tools (`list_datasets`, `get_dataset`, `create_dataset`, `update_dataset`,
  `delete_dataset`), and dataset payloads now use dedicated `Dataset`, `NewDataset`, and `EditDataset` schemas in
  the OpenAPI spec instead of reusing `Material` / `NewProduct` / `EditProduct`.

  `dataSource` is no longer part of the Dataset surface. The previously documented `dataSource: "database"` value
  has been removed from dataset responses, and sending a `dataSource` field in a dataset request body has no effect

  * the server always treats the resource as a dataset based on the route. Clients that read or write `dataSource`
    on the `/v1/dataset` endpoints should drop the field.

  The `ProductDataSource` enum used on `/v1/product` and `/v1/material` is narrowed to `"model"` and `"external"`.
  The `"database"` value is no longer accepted on those routes - datasets now have their own endpoint.
</Update>

<Update label="April 7, 2026" description="v3.252.0">
  ## Organization assignment on user invite

  Admins can now assign an organization when inviting new users. The org selector appears in the invite
  modal and the backend creates the membership in the same transaction as user creation.
</Update>

<Update label="April 3, 2026" description="v3.247.0">
  ## Role-based access control

  A new [Access Control](https://app.variable.global/access-control) page gives a clear overview of what each role

  * Owner, Admin, Contributor, and Viewer - can access across your organization. Permissions are organized into five
    categories:

  * **Products** - LCAs and Declarations (All roles)

  * **Inventory** - Materials, Energy, Transport, Process, Datasets, and Activities (All roles)

  * **Exchange** - Elements, Suppliers, Customers, Requests, and Deliveries (Owner and Admin only)

  * **Reports & Plans** - Analyze, Report, and Plan (Owner and Admin only)

  * **Account** - Org, Users, Locations, Company Settings, and more (Mixed access)
</Update>

<Update label="March 2026" description="v3.245.0">
  ## Extended org access control

  Organization-level access control now covers transports, datasets, parts, and EPDs in addition to products and
  materials.

  ## Self-service dataset copying

  Users can duplicate database datasets into their own company, enabling custom modifications. <Badge color="yellow" size="sm">Preview</Badge>

  ## Second indicator in modelling

  The modelling screen now supports a secondary impact indicator alongside GWP, allowing side-by-side comparison of
  environmental impacts. Sorting by the second indicator is also supported.

  ## Taxonomy enforcement and dataset alignment

  Taxonomy alignment is now enforced when assigning datasets to inventory items. A mismatch indicator shows
  when a dataset's taxonomy differs from the input and offers a one-click update action.

  ## PERM/PENRM calculation for classified materials

  Impact calculations now include PERM (primary energy from renewable materials) and PENRM (primary energy from
  non-renewable materials) for material inputs classified with flow types.

  ## BCCP and BCCAP rules

  The impact calculation service now applies biogenic carbon content rules (BCCP for products, BCCAP for packaging)
  based on functional group classification, per EN 15804+A2.

  ## Geographic filtering

  Search results can now be filtered by geography, making it easier to find region-specific datasets and products.

  ## A1-A3 entry mode toggle for custom datasets

  Custom datasets now support toggling between individual A1, A2, A3 stage entry and a combined A1-A3 entry mode.

  ## Decimal separator override for data imports

  Data imports now support explicit decimal separator selection, resolving ambiguity with European-format CSV files.

  ## Self-hosted data package delivery

  Self-hosted instances now receive emission factor data packages via mounted volumes, simplifying data updates.
</Update>

<Update label="February 2026" description="v3.210.0">
  ## Versioning in Public API

  The public API now supports versioning for products and materials. Create new versions, list version history,
  and fetch specific versions via the API. Bulk endpoints updated accordingly. <Badge color="yellow" size="sm">Preview</Badge>

  ## `varId` added to Public API

  All API responses now include the `varId` field - a human-readable, short identifier for each entity.
  <Badge color="yellow" size="sm">Preview</Badge>

  ## BOM import adapters

  A new adapter pattern for BOM imports allows external formats (e.g., SAP S/4HANA) to be automatically
  transformed into the expected CSV structure on upload.
</Update>

<Update label="January 2026" description="v3.164.0">
  ## Bulk endpoints in Public API

  New bulk create, update, and delete endpoints added for products, materials, and activities, allowing efficient
  batch operations via the API.

  ## Manual geolocation entry

  Users can manually enter geographic coordinates with source tracking, useful when automated geocoding is
  unavailable.
</Update>
