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

# LCA Scenarios

> Model design changes on a copy of an LCA and compare them with the original

[← LCA API](https://code.dcycle.io/api-reference/lca/overview.md)

A scenario answers "what would this design change do to my product's impact?". It is a draft copy of a
**baseline** LCA. You group your edits in named **design changes** (for example "Recycled PET" or "Local
sourcing"), calculate the difference with the baseline, and get it split per design change. The baseline is never
modified.

> [!NOTE]
>
> Scenario modelling is enabled per organization. When it is off, every route on this page answers `403`
> `SCENARIO_MODELLING_DISABLED`. Ask your Dcycle contact to enable it.

## How it works

1. **Create a scenario** from a saved LCA. It is an LCA of its own: its `scenario_id` is also an `lca_id`, so its
   blocks are read with the usual LCA routes.
2. **Add a design change.** It becomes the *active* one: every edit you write from then on is logged under it.
3. **Edit the scenario** with the regular LCA write routes (listed [below](#edits-a-scenario-accepts)), on the
   scenario's id. A scenario only changes **values**: adding or removing blocks or rows answers `409`
   `SCENARIO_EDIT_NOT_SUPPORTED`. Replacing one row's material by another (a swap) is allowed.
4. **Compare.** `POST .../scenario/comparison` recalculates both LCAs when needed and answers each impact
   category's baseline and scenario totals, what each design change contributes, and the `other_effects` no design
   change explains. The contributions and `other_effects` add up to the difference.
5. **Keep or discard it.** Saving a scenario turns it into an LCA of the portfolio, linked to its baseline.
   Discarding deletes a draft.

All routes take the same headers as the rest of the LCA API: `x-api-key` and `x-organization-id`.

## Endpoints

| Method | Path | What it does |
| - | - | - |
| `POST` | `/v1/lca/{lca_id}/scenarios` | Create a draft scenario from the LCA. Body: `{"name"?}`. `201` |
| `GET` | `/v1/lca/{lca_id}/scenarios` | The draft and saved scenarios of a baseline, oldest first |
| `GET` | `/v1/lca/{scenario_id}/scenario` | The scenario with its baseline, design changes and their edits |
| `POST` | `/v1/lca/{scenario_id}/design-changes` | Add a design change; it becomes the active one. Body: `{"name"}`. `201` |
| `PATCH` | `/v1/lca/{scenario_id}/design-changes/{change_id}` | Rename it and/or activate it. Body: `{"name"?, "active"?: true}` |
| `DELETE` | `/v1/lca/{scenario_id}/design-changes/{change_id}` | Delete an empty design change. `204` |
| `PATCH` | `/v1/lca/{scenario_id}/edits/{edit_id}` | Give a logged edit a new value. Body: `{"value"}`. `204` when the value undoes it |
| `DELETE` | `/v1/lca/{scenario_id}/edits/{edit_id}` | Undo a logged edit. `204` |
| `POST` | `/v1/lca/{scenario_id}/scenario/comparison` | Recalculate when needed and compare with the baseline |
| `GET` | `/v1/lca/{scenario_id}/scenario/comparison` | The last comparison, without recalculating |
| `POST` | `/v1/lca/{scenario_id}/scenario/save` | Keep the scenario as an LCA. Body: `{"name", "description"?}` |
| `DELETE` | `/v1/lca/{scenario_id}/scenario` | Discard a draft scenario. `204` |

### Edits a scenario accepts

These writes, sent to a scenario, are logged as edits of the active design change. Pass
`?design_change_id={change_id}` to log one under another design change without activating it.

| Method | Path | Edit |
| - | - | - |
| `PATCH` | `/v1/lca/{scenario_id}/material/{block_id}` | Quantities and units of a material block's rows, or one row's material swapped |
| `PATCH` | `/v1/lca/{scenario_id}/process/{block_id}` | Quantities and units of a process's inputs, or one input swapped |
| `PUT` | `/lca/{scenario_id}/transport/{block_id}/legs` | A material's transport legs (`scope: local`) |
| `PATCH` | `/lca/distributions/{block_id}` | Distribution quantities |
| `PUT` | `/lca/product_use/{block_id}` | Use phase: lifespan, electricity, water, fuel |

A material block save sends the block's full row list, as `GET /lca/{lca_id}/block_materials/{block_id}` returns
it. Send each row back with its `id` (the relation id) and change only the values you mean to change. To swap a
material, replace that row by the new resource with the same `quantity` and `unit_id` and **no** `id`:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "name": "Packaging",
  "materials": [
    {"material_id": "<recycled PET id>", "quantity": 0.4, "unit_id": "<kg id>",
     "name": "Recycled PET", "type": "material", "country": "ES"},
    {"id": "<relation id>", "material_id": "<cardboard id>", "quantity": 0.1, "unit_id": "<kg id>",
     "name": "Cardboard", "type": "material", "country": "ES"}
  ]
}
```

## Example

```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
API=https://api.dcycle.io
H=(-H "x-api-key: ${DCYCLE_API_KEY}" -H "x-organization-id: ${DCYCLE_ORG_ID}" -H "content-type: application/json")

# 1. Copy the LCA into a scenario
SCENARIO_ID=$(curl -s -X POST "$API/v1/lca/${LCA_ID}/scenarios" "${H[@]}" -d '{"name": "Bottle v2"}' | jq -r .id)

# 2. Open a design change: the next edits are logged under it
curl -s -X POST "$API/v1/lca/${SCENARIO_ID}/design-changes" "${H[@]}" -d '{"name": "Lighter bottle"}'

# 3. Edit the scenario with the regular routes (here: a material block's quantities)
curl -s -X PATCH "$API/v1/lca/${SCENARIO_ID}/material/${BLOCK_ID}" "${H[@]}" -d @block.json

# 4. Compare with the baseline
curl -s -X POST "$API/v1/lca/${SCENARIO_ID}/scenario/comparison" "${H[@]}"
```

## Comparison response

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "view": "system",
  "stale_reason": null,
  "baseline": {"id": "4b1b8d5b-01f0-455b-bc41-0b8213cf57cb", "name": "Bottle"},
  "scenario": {"id": "0d0c446d-0539-428a-9423-636e0273fa4f", "name": "Bottle v2"},
  "compared_at": "2026-11-11T10:45:23.921738",
  "categories": [
    {"impact_category": "climate_change", "unit": "kg CO2-Eq", "baseline": 8999.57, "scenario": 8991.69}
  ],
  "design_changes": [
    {"id": "dfb93b48-6529-430b-b06b-e3de743ad982", "name": "Recycled PET",
     "contributions": {"climate_change": -7.46}}
  ],
  "other_effects": {"climate_change": -0.42}
}
```

**categories** `array`

Per impact category: `baseline` and `scenario` are each LCA's total over every stage (the canvas Total Impact).

**design\_changes\[].contributions** `object`

Impact category → what the design change adds to the scenario over the baseline. A material counts for the design
change that edited it last; the rest of a block counts for its last block-level edit.

**other\_effects** `object`

Impact category → the part of the difference no design change explains (for example an emission factor updated
since the copy). The contributions and the other effects add up to `scenario - baseline`.

**stale\_reason** `string`

On the `GET`: `edits` when the scenario changed since this comparison, `baseline_changed` when only the baseline
did, `null` while it is current.

## Errors

| Status | Code | Meaning |
| - | - | - |
| 403 | `SCENARIO_MODELLING_DISABLED` | Scenario modelling is not enabled for the organization |
| 404 | `NO_COMPARISON_YET` | `GET` comparison of a scenario never compared: `POST` it first |
| 409 | `SCENARIO_BASELINE_IS_DRAFT` | Creating a scenario from a draft scenario: save it first |
| 409 | `NO_ACTIVE_DESIGN_CHANGE` | An edit was sent before any design change exists |
| 409 | `SCENARIO_EDIT_NOT_SUPPORTED` | The write is not one a scenario models (adding or removing blocks or rows, imports...) |
| 409 | `SCENARIO_GLOBAL_TRANSPORT` | A transport edit with a scope wider than the block |
| 409 | `EDIT_OVERRIDDEN_BY_LATER_CHANGE` | A later design change edited the same field: only the latest edit can change |
| 409 | `DESIGN_CHANGE_HAS_EDITS` | Deleting a design change that still has edits: undo them first |
| 409 | `SCENARIO_ALREADY_SAVED` | Discarding a saved scenario: delete it as an LCA instead |
| 409 | `BASELINE_MISSING` | Comparing a scenario whose baseline was deleted |

## Related

* [MCP: scenario modelling](https://code.dcycle.io/mcp/lca.md#scenario-modelling): the same workflow from an AI assistant
* [CLI: scenarios](https://code.dcycle.io/cli/lca.md#scenarios): the same workflow from the terminal with `dcy lca scenario`
* [Delete LCA](https://code.dcycle.io/api-reference/lca/delete.md): deletes a saved scenario


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.