# get_utilization_by_tenure

## What it answers

Get named hours, capacity and utilization per Tenure band and per person, by month or Monday-start week and for the whole window. Optional unit is months (default) or weeks. Required real YYYY-MM-DD range.from/range.to dates select inclusive containing periods. periods holds overall and per-band figures for each period; window adds one row per person (at most 500 with explicit truncation). Tenure band is under-1-year, 1-to-3-years, 3-years-or-more or unknown, measured from the person's earliest included payroll start date; the start date itself is never returned. Utilization is summed delivery hours divided by summed gross capacity. No compensation. Read definitions, caveats and truncation before interpreting figures. Decimal strings preserve served numeric figures. 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` | — |
| `range` | object | yes | — | — |
| `range.from` | string | yes | — | pattern `^\d{4}-\d{2}-\d{2}$` |
| `range.to` | string | yes | — | pattern `^\d{4}-\d{2}-\d{2}$` |
| `unit` | string | no | `"months"` | `"months"`, `"weeks"` |

## 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.account`: string (uuid)
  - `data.unit`: string (`"months"`, `"weeks"`)
  - `data.from`: string (pattern `^\d{4}-\d{2}-\d{2}$`)
  - `data.to`: string (pattern `^\d{4}-\d{2}-\d{2}$`)
  - `data.window`: object
    - `data.window.overall`: object
      - `data.window.overall.grossCapacity`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.window.overall.deliveryCapacity`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.window.overall.deliveryHours`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.window.overall.utilization`: string (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.window.tenureBands`: array of object
      - `data.window.tenureBands[].grossCapacity`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.window.tenureBands[].deliveryCapacity`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.window.tenureBands[].deliveryHours`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.window.tenureBands[].utilization`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.window.tenureBands[].tenureBand`: string (`"under-1-year"`, `"1-to-3-years"`, `"3-years-or-more"`, `"unknown"`)
    - `data.window.people`: array of object
      - `data.window.people[].grossCapacity`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.window.people[].deliveryCapacity`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.window.people[].deliveryHours`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.window.people[].utilization`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.window.people[].tenureBand`: string (`"under-1-year"`, `"1-to-3-years"`, `"3-years-or-more"`, `"unknown"`)
      - `data.window.people[].employeeName`: string
  - `data.periods`: array of object
    - `data.periods[].periodStart`: string (pattern `^\d{4}-\d{2}-\d{2}$`)
    - `data.periods[].overall`: same shape as `data.window.overall`
    - `data.periods[].tenureBands`: array of object
      - `data.periods[].tenureBands[]`: same shape as `data.window.tenureBands[]`
  - `data.truncation`: object
    - `data.truncation.limit`: number (`500`)
    - `data.truncation.totalRows`: integer (`minimum: 0`)
    - `data.truncation.returnedRows`: integer (`minimum: 0`)
    - `data.truncation.omittedRows`: integer (`minimum: 0`)
    - `data.truncation.message`: string or null
  - `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.definitions`: map of string to string, optional
    - `data.definitions.*`: string
  - `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

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). It is a map of string to string: each key names a field and each value is its definition text.

## 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_by_tenure",
  "arguments": {
    "range": {
      "from": "2026-01-01",
      "to": "2026-12-31"
    }
  }
}
```

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