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

# Energy Model Runs

> Create, edit, and rerun saved energy models

# Energy Model Runs

`POST /energymodels` accepts an `EnergyModelInput` payload and executes a run
either synchronously or asynchronously. Saved energy-model rows can then be
edited and rerun in place with `GET /energymodels/{id}?include=inputs`,
`PUT /energymodels/{id}`, and `POST /energymodels/{id}/run`.

## Endpoint behavior

* Sync is the default when `async` is omitted.
* `?async=false` runs sync and returns `200` when results complete in request
  scope.
* `?async=true` queues background processing and returns a queued `200`
  response.
* Multi-block sync fan-out can also return a queued `200` response if worker
  blocks do not finish within the sync wait window.
* Queued async and fan-out responses do not expose per-block task IDs in the
  public payload.

## Saved-input edit/rerun workflow

The saved-row workflow is:

1. Fetch the currently saved input JSON with `GET /energymodels/{id}?include=inputs`
2. Edit the returned `inputs` JSON
3. Save the updated JSON back to the same row with `PUT /energymodels/{id}`
4. Rerun that same saved row with `POST /energymodels/{id}/run`

Important contract notes:

* `PUT /energymodels/{id}` updates the saved input JSON on the existing energy-model row.
* `POST /energymodels/{id}/run` runs the currently saved JSON in place on that same row.
* Rerun does not create a new energy-model row.
* Imported PVsyst variants created by `POST /projects/import` are intended entry points for this flow.
* Active rows with status `pending`, `queued`, or `running` reject update/rerun with `409 Conflict`.

## Request contract

Minimum required structure:

* exactly one of `projectId`, `locationId`, or inline `location`
* `blocks` with at least one block

Each block requires:

* `inverterId`
* `shading`, `losses`, `acSystem`, `albedo`
* at least one of `array` or `dcInputs`
* at least one of `layout` or `layoutRef`

## Request compatibility and deprecation behavior

Current behavior:

* top-level `name`
  * backward-compatible alias for `output.name`
* `output.blockIndex`
  * canonical zero-based selected-block response filter
  * valid only when block-level results are effectively requested
* `output.blockResults`
  * canonical block-level time-series visibility flag for `blockTimeSeries`
  * mutually exclusive with `output.fullTimeSeries`
* `output.lossBreakdownTimestamps`
  * top-level boolean request flag
  * implies block-level results are surfaced
  * the older object wrapper form is rejected on the public API
* `output.irradianceLossDetail`
  * additive annual-detail flag that can surface `losses.irradianceLossDetail`
    even when `output.fullOutput` is false
* query params:
  * `blockresults`
* body fields:
  * `blockQuery`
  * `blockQueryIndex`
  * these are compatibility-only selected-block aliases and remain deprecated
* legacy-only `blockQuery` + `blockQueryIndex` still map to canonical
  `output.blockIndex` behavior and emit deprecation warnings
* if canonical `output.blockIndex` and legacy block selectors are both present
  and semantically consistent, the request is accepted and legacy warnings
  still apply
* if canonical `output.blockIndex` and legacy block selectors conflict, the API
  returns `422`
* `output.query` is no longer supported and returns `422`
* object-shaped `output.lossBreakdownTimestamps` values are no longer
  supported and return `422`

Route-specific note:

* `POST /energymodels?debug_block=...` is no longer supported and returns
  `422` with guidance to use `output.blockIndex`.
* `POST /energymodels/{id}/run?debug_block=...` is also no longer supported and
  returns `422` with the same guidance.

Rejected legacy query params:

* `summary`
* `fullresults`

Those return `422` with endpoint-specific guidance.

## Example request

```json theme={null}
{
  "name": "Legacy-Compatible Run Name",
  "locationId": 1,
  "blocks": [
    {
      "inverterId": 1,
      "moduleId": 1,
      "array": {"stringCount": 10, "stringLength": 28},
      "layout": {"arrayType": "fixed", "gcr": 0.4, "azimuth": 180, "axisTilt": 0, "tilt": 25},
      "shading": {"farShading": false, "nearShading": "unlimited", "electricalImpact": false},
      "losses": {
        "moduleQuality": 0.0,
        "mismatch": 0.02,
        "dcohmic": 0.01,
        "lid": 0.01,
        "stringVoltageMismatch": 0.01,
        "invAuxLoss": true,
        "uC": 25,
        "uV": 1.2
      },
      "acSystem": {
        "numberOfInverters": 1,
        "losses": {
          "acohmicLoss": 0.01,
          "transformer": {"ironLoss": 0.005, "copperLoss": 0.005}
        }
      },
      "albedo": {
        "jan": 0.2, "feb": 0.2, "mar": 0.2, "apr": 0.2, "may": 0.2, "jun": 0.2,
        "jul": 0.2, "aug": 0.2, "sep": 0.2, "oct": 0.2, "nov": 0.2, "dec": 0.2
      }
    }
  ],
  "output": {
    "timeSeries": true,
    "blockResults": true,
    "blockIndex": 0,
    "lossBreakdownTimestamps": false
  }
}
```

## Output-shape behavior

* Sync POST shape is controlled by request-body `output` flags.
* Detail GET shape is controlled by `include=...`.
* List GET does not expand shape; `include=summary` is only a compatibility
  token.
* `output.timeSeries=true` exposes plant-level `timeSeries` in sync completed
  responses.
* `output.blockResults` controls `blockTimeSeries` visibility and does not
  rewrite runtime execution scope.
* `output.blockIndex` filters the returned `blockTimeSeries` section only; it
  does not convert the request into single-block execution.
* `output.lossBreakdownTimestamps=true` can surface `blockTimeSeries` even when
  `output.blockResults` is omitted or false.
* `blockTimeSeries` keys preserve original zero-based block numbering,
  including filtered single-block responses.
* timestamped loss details live under
  `blockTimeSeries[<blockIndex>].lossBreakdown` when
  `output.lossBreakdownTimestamps=true`.
* `losses.irradianceLossDetail` is additive and may appear even when
  `output.fullOutput` is false if `output.irradianceLossDetail=true` is set.

Important compatibility note:

* `output.fullTimeSeries=true` can still cause richer stored outputs, but the
  immediate sync POST response filters `timeSeries` back to the six legacy base
  keys.

Async note:

* Async energy-model responses expose only the parent `taskId` in
  `taskDetails`.
* Fan-out child block tasks and their IDs are internal orchestration details
  and are not returned on the public API.

## Import -> edit -> rerun example

This example shows the intended workflow for an imported PVsyst project
template. The same `energy_model_id` is reused across the `GET`, `PUT`, and
`POST /run` steps.

```bash theme={null}
import_result=$(curl -sS -X POST "$DALY_API_BASE_URL/projects/import" \
  -H "Authorization: Bearer $DALY_TOKEN" \
  -F "files=@/path/MyProject.PRJ" \
  -F "files=@/path/Variant.VC")

energy_model_id=$(printf '%s' "$import_result" | jq -r '.energyModels[0].energyModelId')

curl -sS "$DALY_API_BASE_URL/energymodels/$energy_model_id?include=inputs" \
  -H "Authorization: Bearer $DALY_TOKEN"

curl -sS -X PUT "$DALY_API_BASE_URL/energymodels/$energy_model_id" \
  -H "Authorization: Bearer $DALY_TOKEN" \
  -H "Content-Type: application/json" \
  -d @edited-energy-model-input.json

curl -sS -X POST "$DALY_API_BASE_URL/energymodels/$energy_model_id/run?async=true" \
  -H "Authorization: Bearer $DALY_TOKEN"
```

The final rerun response still points at the same saved row:

```json theme={null}
{
  "result": "success",
  "message": "Energy model queued for processing",
  "energyModelId": 56,
  "revision": "v0003",
  "taskDetails": {
    "taskId": 1234,
    "executionMode": "async_queued"
  }
}
```

## Response contract

Canonical response envelopes and include behavior are documented here:

* [Energy Model Response Contracts](/api-reference/energy-model-response-contracts)

## Follow-up calls

* Poll task: `GET /tasks/{taskId}` where `taskId = taskDetails.taskId`
* Fetch run resource: `GET /energymodels/{energyModelId}`
