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

# Reports

> Read and edit reports and report rows through the Fyvel MCP server.

# Reports

The report tools read and edit reports in Fyvel. You can list reports, load definitions, retrieve calculated values, run drill-downs, and change report rows in a structured way.

## Available tools

* `list_reports`
* `create_report`
* `delete_report`
* `toggle_report_star`
* `get_report`
* `resolve_report`
* `resolve_report_row_drilldown`
* `apply_report_rows`
* `reorder_report_rows`
* `delete_report_row`

## Scope

Reports are **organisation-wide**. You do not need a `companyUuid`.

## Recommended flow

**Load the structure**

1. `list_reports`
2. choose the matching `reportUuid`
3. `get_report` — returns rows, row UUIDs (`reportRowUuid`), order, and configuration

**Check the numbers**

4. `list_layers` — load booking layers for the company (`layer_uuid`, `is_default`, `is_forecast`)
5. `resolve_report` — calculated values per row and period (see [Resolve modes](#resolve-modes) and [Analytics cache](#analytics-cache-and-retry) below)
6. if needed, `resolve_report_row_drilldown` — **the same mode and the same layer configuration** as the related `resolve_report`

## Analytics cache and retry

Booking-based report values come from an **analytics cache** that Fyvel rebuilds in the background after changes to bookings or mappings (FastAPI microservice).

If `resolve_report` finds only empty values (`null` or `0`) on every **data and calculated row**, the tool responds with an **error** instead of a success response:

```
Report data is still being refreshed. Wait a few seconds and call this tool again.
```

The server marks the cache of the affected company as stale. On **retry** it is usually rebuilt and then returns real numbers.

**Important for agents:**

* Do **not** treat this error as an empty report — wait briefly and repeat **the same call**.
* Especially after `set_booking_category`, `accept_ai_mapping_suggestions`, `append_manual_transactions`, and similar: only analyse once `resolve_report` returns numbers successfully.
* Header and spacer rows (`header`, `spacer`) do not count — only `data` and `calculated` rows are included in the check.
* `resolve_report_row_drilldown` does **not** use the refresh-pending check: `rows: []` means no categories or accounts with booking data match the row. No retry is needed.

## Resolve modes

`resolve_report` and `resolve_report_row_drilldown` have three mutually exclusive modes — the same logic as **Settings** in the report view of the web app.

| Mode | Parameters | Layers |
| - | - | - |
| **Standard** (default) | `selectedLayerUuids` and/or `selectedLayerGroupUuids` | one flat selection for the whole period |
| **Rolling forecast** | `periodSlices` | its own layers per segment (`actual` / `forecast`) |
| **Comparison** | `compareSides` | `sideA` and `sideB` separately; the result is **sideA − sideB** |

`periodSlices` and `compareSides` must **not** be set at the same time. In standard mode without layers, the resolver returns only zeros.

### Standard

Typical actual layers: `is_default: true`, not a snapshot (`layer_type` not equal to `snapshot`). Groups via `list_layer_groups` → `selectedLayerGroupUuids`.

```json theme={null}
{
  "reportUuid": "<report-uuid>",
  "companyUuid": "<company-uuid>",
  "periodFrom": "2025-01",
  "periodTo": "2025-12",
  "aggregationMode": "monthly",
  "selectedLayerUuids": ["<default-actual-layer-uuid>"]
}
```

### Rolling forecast

With `periodSlices` you combine actual and forecast segments. Usual case: two segments — actuals through the cutoff date, forecast from the following month. Slice periods are monthly keys (`YYYY-MM`), even when `aggregationMode` is quarterly or yearly.

```json theme={null}
{
  "reportUuid": "<report-uuid>",
  "companyUuid": "<company-uuid>",
  "periodFrom": "2025-01",
  "periodTo": "2025-12",
  "aggregationMode": "monthly",
  "periodSlices": [
    {
      "periodFrom": "2025-01",
      "periodTo": "2025-06",
      "segmentKind": "actual",
      "selectedLayerUuids": ["<actual-layer-uuid>"]
    },
    {
      "periodFrom": "2025-07",
      "periodTo": "2025-12",
      "segmentKind": "forecast",
      "selectedLayerUuids": ["<forecast-layer-uuid>"]
    }
  ]
}
```

The response includes `period_forecast` with flags per column (`has_forecast`, `has_historical`, `is_mixed`).

### Comparison

Place two layer configurations next to each other — for example actual vs. plan, or the current state vs. a snapshot.

```json theme={null}
{
  "reportUuid": "<report-uuid>",
  "companyUuid": "<company-uuid>",
  "periodFrom": "2025-01",
  "periodTo": "2025-12",
  "compareSides": {
    "sideA": { "label": "Actuals", "selectedLayerUuids": ["<actual-layer-uuid>"] },
    "sideB": { "label": "Plan", "selectedLayerUuids": ["<plan-layer-uuid>"] }
  }
}
```

Each side can use `selectedLayerUuids` and/or `selectedLayerGroupUuids`. In comparison mode the `period_forecast` flags in the response are reset.

**Change rows**

7. Send new or changed rows via `apply_report_rows`
8. Change only the order: `reorder_report_rows` with every row UUID in the order you want
9. Delete a single row: `delete_report_row`

## Batch rows (`apply_report_rows`)

With `apply_report_rows` you can create or update several rows in one call.

* **Row types**: including `data_pl`, `data_bs`, `data_cashflow`, `data_cashflow_capex`, `data_cashflow_equity_change`, `calculated`, `header`, `spacer`
* **New rows**: omit `reportRowUuid`
* **Existing rows**: pass the existing `reportRowUuid`
* **Limit**: at most 60 rows per request
* **References to new rows**: use `clientKey` and `refClientKey` when calculated rows in the same request refer to rows created in that request

For signs: `data_pl`, `data_cashflow`, `data_cashflow_capex`, and `data_cashflow_equity_change` optionally use `changeSign` relative to the default. `data_cashflow_capex` expects `fixedAssetChangeSources` (balance-sheet delta) and `depreciationSources` (P\&L period value). `data_cashflow_equity_change` expects only `shareholderEquityChangeSources` (balance-sheet delta). Cumulative P\&L result (YTD) and net income (period) are calculated automatically across the entire P\&L. `data_bs` and `calculated` use `signConvention`. You can check the result with `resolve_report`.

## Typical use cases

* list and favourite reports (`toggle_report_star`)
* create or delete reports
* load report structure and row UUIDs (`get_report`)
* get numbers and forecast flags from the resolver (`resolve_report`) — standard, rolling forecast, or comparison
* compare actual vs. plan or a snapshot (`compareSides`)
* combine actual and forecast periods across a cutoff date (`periodSlices`)
* break data rows down in depth (`resolve_report_row_drilldown`)
* create or adjust several rows at once (`apply_report_rows`)
* set the overall order (`reorder_report_rows`)
* remove a single row (`delete_report_row`)


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