# estimate_project

## What it answers

Run the Scope Predictor estimator for a described project: hours per task category fitted to the comparable projects you name (projectNames, at most 10, by Project Name from list_project_entries), the likely range, how similar each of the account's projects is to the brief, the account's ABR target and, when a range is given and the owner may see payroll, Direct Delivery Margin. targetAgi is required. Same calculation and figures as the Scope Predictor page for the same inputs. It estimates only: nothing is saved (list_saved_estimates and get_saved_estimate read what people saved). Direct Delivery Margin is derived from compensation: it is returned only when the current connection owner has People (payroll) grid view on this account, otherwise it is omitted with a compensation-withheld caveat; when it cannot be computed it says why in reasons. Numbers are decimal strings. 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` | — |
| `projectNames` | array of string | yes | — | `maxItems: 10` |
| `targetAgi` | string | yes | — | pattern `^-?\d+(?:\.\d+)?$` |
| `overrides` | map of string to string | no | — | — |
| `brief` | object | no | — | — |
| `brief.serviceLine` | string | no | — | `maxLength: 200` |
| `brief.product` | string | no | — | `maxLength: 200` |
| `brief.client` | string | no | — | `maxLength: 200` |
| `brief.durationDays` | string | no | — | pattern `^-?\d+(?:\.\d+)?$` |
| `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}$` |

## 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.targetAgi`: string (pattern `^-?\d+(?:\.\d+)?$`)
  - `data.comparables`: array of object
    - `data.comparables[].project`: string
    - `data.comparables[].client`: string or null, optional
    - `data.comparables[].product`: string or null, optional
    - `data.comparables[].serviceLine`: string or null, optional
    - `data.comparables[].status`: string or null, optional
    - `data.comparables[].agi`: string (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.comparables[].plannedHours`: string (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.comparables[].actualHours`: string (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.comparables[].plannedAbr`: string (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.comparables[].actualAbr`: string or null, optional (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.comparables[].startDate`: string or null, optional
    - `data.comparables[].firstUsedDate`: string or null, optional
    - `data.comparables[].endDate`: string or null, optional
    - `data.comparables[].lastUsedDate`: string or null, optional
  - `data.taskCategories`: array of string
  - `data.actuals`: array of object
    - `data.actuals[].project`: string
    - `data.actuals[].overallHours`: string (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.actuals[].taskCategories`: map of string to string
      - `data.actuals[].taskCategories.*`: string (pattern `^-?\d+(?:\.\d+)?$`)
  - `data.plans`: array of object
    - `data.plans[]`: same shape as `data.actuals[]`
  - `data.regressions`: array of object
    - `data.regressions[].taskCategory`: string
    - `data.regressions[].slope`: string (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.regressions[].intercept`: string (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.regressions[].predictedHours`: string (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.regressions[].determinedHours`: string (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.regressions[].overridden`: boolean
  - `data.totalDeterminedHours`: string (pattern `^-?\d+(?:\.\d+)?$`)
  - `data.estimatedAbr`: string (pattern `^-?\d+(?:\.\d+)?$`)
  - `data.likelyRange`: object
    - `data.likelyRange.status`: string (`"available"`, `"approximate"`, `"unavailable"`)
    - `data.likelyRange.usableComparables`: string (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.likelyRange.excludedComparables`: array of object
      - `data.likelyRange.excludedComparables[].project`: string
      - `data.likelyRange.excludedComparables[].agi`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.likelyRange.excludedComparables[].reason`: string (`"non-positive-agi"`)
    - `data.likelyRange.reason`: string or null
    - `data.likelyRange.categories`: array of object
      - `data.likelyRange.categories[].taskCategory`: string
      - `data.likelyRange.categories[].low`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.likelyRange.categories[].middle`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.likelyRange.categories[].high`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.likelyRange.categories[].comparablesUsed`: string (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.likelyRange.total`: object or null
      - `data.likelyRange.total.low`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.likelyRange.total.middle`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.likelyRange.total.high`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - `data.likelyRange.total.comparablesUsed`: string (pattern `^-?\d+(?:\.\d+)?$`)
  - `data.similarity`: object
    - `data.similarity.pointsPossible`: string (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.similarity.projects`: array of object
      - `data.similarity.projects[].project`: string
      - `data.similarity.projects[].score`: string (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.similarity.closest`: array of object
      - `data.similarity.closest[]`: same shape as `data.similarity.projects[]`
  - `data.abrTarget`: object or null
    - `data.abrTarget.value`: string (pattern `^-?\d+(?:\.\d+)?$`)
    - `data.abrTarget.months`: array of string
  - `data.directDeliveryMargin`: object, optional
    - One of:
      - Variant 1 (`status` is `"available"`):
        - `data.directDeliveryMargin.status`: string (`"available"`)
        - `data.directDeliveryMargin.value`: string (pattern `^-?\d+(?:\.\d+)?$`)
        - `data.directDeliveryMargin.directDeliveryCosts`: string (pattern `^-?\d+(?:\.\d+)?$`)
        - `data.directDeliveryMargin.targetAgi`: string (pattern `^-?\d+(?:\.\d+)?$`)
        - `data.directDeliveryMargin.costWindow`: object
          - `data.directDeliveryMargin.costWindow.startDate`: string (pattern `^\d{4}-\d{2}-\d{2}$`)
          - `data.directDeliveryMargin.costWindow.endDate`: string (pattern `^\d{4}-\d{2}-\d{2}$`)
        - `data.directDeliveryMargin.categories`: array of object
          - `data.directDeliveryMargin.categories[].taskCategory`: string
          - `data.directDeliveryMargin.categories[].estimatedHours`: string (pattern `^-?\d+(?:\.\d+)?$`)
          - `data.directDeliveryMargin.categories[].hoursSource`: string (`"override"`, `"median"`)
          - `data.directDeliveryMargin.categories[].costPerDeliveryHour`: string (pattern `^-?\d+(?:\.\d+)?$`)
          - `data.directDeliveryMargin.categories[].directDeliveryCost`: string (pattern `^-?\d+(?:\.\d+)?$`)
      - Variant 2 (`status` is `"unavailable"`):
        - `data.directDeliveryMargin.status`: string (`"unavailable"`)
        - `data.directDeliveryMargin.reasons`: array of string
        - `data.directDeliveryMargin.categoriesWithoutCostPerDeliveryHour`: array of string
        - `data.directDeliveryMargin.categoriesWithoutEstimatedHours`: array of string
  - `data.definitions`: map of string to string, optional
    - `data.definitions.*`: string
  - `data.caveats`: array of object
    - `data.caveats[].code`: string
    - `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

Codes are not enumerated in the schema; read each `code` and `message`.

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

## Example call

```json
{
  "name": "estimate_project",
  "arguments": {
    "projectNames": [],
    "targetAgi": "50000",
    "range": {
      "from": "2026-01-01",
      "to": "2026-03-31"
    }
  }
}
```

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