# get_utilization_trend

## What it answers

Get served utilization by month or week, optionally by role category. Optional unit is months (default) or weeks. Optional range.from and range.to are required together as real YYYY-MM-DD dates and select period starts inclusively without recalculating rolling figures. Defaults to the latest 12 months or 26 weeks; always reports full available range. Named hours only, no compensation. availableRange spans all served period starts even when the selected range has no rows, or is null when none exist. Read definitions, caveats and truncation before interpreting figures. Decimal strings preserve served numeric figures; they do not recover precision previously lost by a report. includeRoleCategories defaults to false. With includeRoleCategories true, omittedRoleCategories lists categories with the reason: all their figures are zero in every returned period. The roleCategories catalog remains complete; categories with any non-zero figure, including rolling figures, retain all selected periods. Read parakeeto://methodology for shared response conventions and INVALID_ARGUMENT guidance.

Read-only.

## Arguments

| Argument | Type | Required | Default | Allowed values |
| --- | --- | --- | --- | --- |
| `account` | string (uuid) | no | — | — |
| `includeDefinitions` | boolean | no | `true` | — |
| `unit` | string | no | `"months"` | `"months"`, `"weeks"` |
| `range` | object | no | — | — |
| `range.from` | string | yes | — | pattern `^\d{4}-\d{2}-\d{2}$` |
| `range.to` | string | yes | — | pattern `^\d{4}-\d{2}-\d{2}$` |
| `includeRoleCategories` | boolean | no | `false` | — |

## Returned fields

The result is an envelope: `ok: true` with `data`, or `ok: false` with `error`. Error codes: `MODEL_CALCULATOR_ACCESS_REQUIRED`, `PAYROLL_GRID_MISSING`, `PAYROLL_GRID_REQUIRED`, `PROJECT_GRID_REQUIRED`, `EXPENSE_GRID_REQUIRED`, `PAYROLL_ENTRY_REFERENCE_STALE`, `ACCOUNT_NOT_FOUND`, `ACCOUNT_CONFIGURATION_MISSING`, `NO_IMPORTED_TIME`, `NOTHING_IMPORTED`, `ACCOUNT_REQUIRED`, `NO_REACHABLE_ACCOUNTS`, `FEATURE_NOT_ENTITLED`, `PERMISSION_DENIED`, `CALCULATION_FAILED`, `INTERNAL_ERROR`, `INVALID_ARGUMENT`.

- `data`: object
  - `data.omittedRoleCategories`: object, optional
    - `data.omittedRoleCategories.categories`: array of string
    - `data.omittedRoleCategories.reason`: string (`"All figures are zero in every returned period."`)
  - `data.account`: string (uuid)
  - `data.unit`: string (`"months"`, `"weeks"`)
  - `data.definitions`: object, optional
    - `data.definitions.grossCapacity`: string
    - `data.definitions.deliveryCapacity`: string
    - `data.definitions.deliveryHours`: string
    - `data.definitions.targetUtilization`: string
    - `data.definitions.utilization`: string
    - `data.definitions.utilizationDelta`: string
    - `data.definitions.rollingGrossCapacity`: string
    - `data.definitions.rollingDeliveryCapacity`: string
    - `data.definitions.rollingDeliveryHours`: string
    - `data.definitions.rollingTargetUtilization`: string
    - `data.definitions.rollingUtilization`: string
    - `data.definitions.rollingUtilizationDelta`: string
    - `data.definitions.rollingWindow`: string
    - `data.definitions.annualDaysBasis`: string
    - `data.definitions.range`: string
    - `data.definitions.qualifications`: string
    - `data.definitions.presentation`: string
  - `data.annualDays`: string (pattern `^-?\d+(?:\.\d+)?$`)
  - `data.freshness`: array of object
    - `data.freshness[].source`: string (`"time-entries"`, `"role-task-mappings"`, `"annual-days"`, `"dashboard-inputs"`)
    - `data.freshness[].everCompleted`: boolean
    - `data.freshness[].completedAt`: string (date-time) or null
  - `data.gridFreshness`: array of object
    - `data.gridFreshness[].kind`: string (`"payroll"`, `"expense"`, `"project"`)
    - `data.gridFreshness[].gridId`: string (uuid)
    - `data.gridFreshness[].name`: string
    - `data.gridFreshness[].storage`: string (`"sheet"`, `"database"`)
    - `data.gridFreshness[].sheetRefreshedAt`: string (date-time) or null
  - `data.caveats`: array of object
    - One of:
      - Variant 1 (`code` is `"omission"`):
        - `data.caveats[].code`: string (`"omission"`)
        - `data.caveats[].source`: string (`"payroll"`, `"time"`)
        - `data.caveats[].employeeName`: string, optional
        - `data.caveats[].rowPosition`: integer (`exclusiveMinimum: 0`)
        - `data.caveats[].message`: string
      - Variant 2 (`code` is `"allocation-mismatch"`):
        - `data.caveats[].code`: string (`"allocation-mismatch"`)
        - `data.caveats[].employeeName`: string
        - `data.caveats[].startDate`: string or null (pattern `^\d{4}-\d{2}-\d{2}$`)
        - `data.caveats[].weeklyHours`: string (pattern `^-?\d+(?:\.\d+)?$`)
        - `data.caveats[].targetHoursSum`: string (pattern `^-?\d+(?:\.\d+)?$`)
        - `data.caveats[].message`: string
      - Variant 3 (`code` is `"data-condition"`, `id` is `"payroll-duplicate-start-date-double-count"`):
        - `data.caveats[].code`: string (`"data-condition"`)
        - `data.caveats[].id`: string (`"payroll-duplicate-start-date-double-count"`)
        - `data.caveats[].rowCount`: integer (`exclusiveMinimum: 0`)
        - `data.caveats[].employeeName`: string
        - `data.caveats[].startDate`: string (pattern `^\d{4}-\d{2}-\d{2}$`)
        - `data.caveats[].roleCategories`: array of string
        - `data.caveats[].latestPeriod`: string, optional (pattern `^\d{4}-\d{2}-\d{2}$`)
        - `data.caveats[].periods`: array of object, optional
          - `data.caveats[].periods[].date`: string (pattern `^\d{4}-\d{2}-\d{2}$`)
          - `data.caveats[].periods[].roles`: array of string
          - `data.caveats[].periods[].rollingRoles`: array of string
          - `data.caveats[].periods[].overall`: boolean
          - `data.caveats[].periods[].rollingOverall`: boolean
        - `data.caveats[].teamRoles`: array of string, optional
        - `data.caveats[].message`: string
  - `data.availableRange`: object or null
    - `data.availableRange.from`: string (pattern `^\d{4}-\d{2}-\d{2}$`)
    - `data.availableRange.to`: string (pattern `^\d{4}-\d{2}-\d{2}$`)
  - `data.selectedRange`: object or null
    - `data.selectedRange.from`: string (pattern `^\d{4}-\d{2}-\d{2}$`)
    - `data.selectedRange.to`: string (pattern `^\d{4}-\d{2}-\d{2}$`)
  - `data.periods`: array of object
    - `data.periods[].periodStart`: string (pattern `^\d{4}-\d{2}-\d{2}$`)
    - `data.periods[].overall`: object
      - `data.periods[].overall.grossCapacity`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.periods[].overall.deliveryCapacity`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.periods[].overall.deliveryHours`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.periods[].overall.targetUtilization`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.periods[].overall.utilization`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.periods[].overall.rollingGrossCapacity`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.periods[].overall.rollingDeliveryCapacity`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.periods[].overall.rollingDeliveryHours`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.periods[].overall.utilizationDelta`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.periods[].overall.rollingTargetUtilization`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.periods[].overall.rollingUtilization`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.periods[].overall.rollingUtilizationDelta`: string (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.periods[].roleCategories`: array of object, optional
      - `data.periods[].roleCategories[].roleCategory`: string
      - `data.periods[].roleCategories[].values`: same shape as `data.periods[].overall`
  - `data.roleCategories`: array of string, optional

On failure:

- `error`: object
  - `error.code`: string (`"MODEL_CALCULATOR_ACCESS_REQUIRED"`, `"PAYROLL_GRID_MISSING"`, `"PAYROLL_GRID_REQUIRED"`, `"PROJECT_GRID_REQUIRED"`, `"EXPENSE_GRID_REQUIRED"`, `"PAYROLL_ENTRY_REFERENCE_STALE"`, `"ACCOUNT_NOT_FOUND"`, `"ACCOUNT_CONFIGURATION_MISSING"`, `"NO_IMPORTED_TIME"`, `"NOTHING_IMPORTED"`, `"ACCOUNT_REQUIRED"`, `"NO_REACHABLE_ACCOUNTS"`, `"FEATURE_NOT_ENTITLED"`, `"PERMISSION_DENIED"`, `"CALCULATION_FAILED"`, `"INTERNAL_ERROR"`, `"INVALID_ARGUMENT"`)
  - `error.argument`: string, optional (`minLength: 1`)
  - `error.message`: string
  - `error.accounts`: array of object, optional
    - `error.accounts[].id`: string (uuid)
    - `error.accounts[].name`: string
  - `error.payrollGrids`: array of object, optional
    - `error.payrollGrids[]`: same shape as `error.accounts[]`
  - `error.projectGrids`: array of object, optional
    - `error.projectGrids[]`: same shape as `error.accounts[]`
  - `error.expenseGrids`: array of object, optional
    - `error.expenseGrids[]`: same shape as `error.accounts[]`
  - `error.context`: object, optional
    - `error.context.account`: object
      - Further fields: see the response.
    - `error.context.imports`: array of object
      - Further fields: see the response.
    - `error.context.hasTimeEntries`: boolean
    - `error.context.reports`: array of object
      - Further fields: see the response.

## Definitions

Returned in `definitions` when `includeDefinitions` is true (the default). Each value is the definition text for that field.

- `grossCapacity`
- `deliveryCapacity`
- `deliveryHours`
- `targetUtilization`
- `utilization`
- `utilizationDelta`
- `rollingGrossCapacity`
- `rollingDeliveryCapacity`
- `rollingDeliveryHours`
- `rollingTargetUtilization`
- `rollingUtilization`
- `rollingUtilizationDelta`
- `rollingWindow`
- `annualDaysBasis`
- `range`
- `qualifications`
- `presentation`

## Caveats

- `omission`. Also carries `source`, `employeeName`, `rowPosition`.
- `allocation-mismatch`. Also carries `employeeName`, `startDate`, `weeklyHours`, `targetHoursSum`.
- `data-condition`. Also carries `id`, `rowCount`, `employeeName`, `startDate`, `roleCategories`, `latestPeriod`, `periods`, `teamRoles`.

Read every caveat before interpreting a figure. See [methodology](</methodology.md>) on how to communicate caveats and data-quality conditions.

## Example call

```json
{
  "name": "get_utilization_trend",
  "arguments": {
    "unit": "months"
  }
}
```

Add `account` with an identifier from `list_accounts`; omit it only when the connection reaches exactly one account.
