node-autotask 0.44.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/MCP_TOOL_CATALOG.json +153 -1269
- package/MCP_TOOL_MANIFEST.md +28 -95
- package/MCP_TOOL_OVERRIDES.json +8 -62
- package/N8N_MCP_MANIFEST.json +118 -695
- package/N8N_MCP_MANIFEST.md +32 -52
- package/README.md +5 -10
- package/capabilities.json +23788 -31075
- package/dist/{base-CftidiyZ.d.cts → base-BVoYDlVA.d.cts} +2 -2
- package/dist/{base-CftidiyZ.d.ts → base-BVoYDlVA.d.ts} +2 -2
- package/dist/capabilities.cjs +2 -2
- package/dist/capabilities.js +1 -1
- package/dist/{chunk-N6IWPGHB.js → chunk-ASMCTZO2.js} +23787 -31074
- package/dist/{chunk-6WLGHKIU.cjs → chunk-DC3PY4EF.cjs} +1839 -1407
- package/dist/{chunk-BKTOMNT2.js → chunk-DXKXQHCX.js} +5 -5
- package/dist/{chunk-KWQSGWY5.cjs → chunk-JO6WI763.cjs} +143 -143
- package/dist/{chunk-LYI5FRPX.js → chunk-NSW7JPVS.js} +11544 -13365
- package/dist/{chunk-7MCKJTSA.js → chunk-O4PIPUS4.js} +1583 -1151
- package/dist/{chunk-PH3ESYAF.cjs → chunk-VXVYFJH3.cjs} +23787 -31074
- package/dist/{chunk-EMYDC5SG.cjs → chunk-YMBVKVK4.cjs} +6123 -7944
- package/dist/{index-YbEk0awP.d.cts → index-C5g1H_hF.d.ts} +1194 -1415
- package/dist/{index-D8FtzUGP.d.ts → index-Db0SNnQn.d.cts} +1194 -1415
- package/dist/{index-DgBA1JMH.d.cts → index-c549LvOb.d.cts} +188 -188
- package/dist/{index-DgBA1JMH.d.ts → index-c549LvOb.d.ts} +188 -188
- package/dist/index.cjs +22 -22
- package/dist/index.d.cts +15 -42
- package/dist/index.d.ts +15 -42
- package/dist/index.js +7 -7
- package/dist/operations/index.cjs +5 -2327
- package/dist/operations/index.d.cts +7 -287
- package/dist/operations/index.d.ts +7 -287
- package/dist/operations/index.js +29 -2351
- package/dist/pool.cjs +8 -8
- package/dist/pool.d.cts +1 -1
- package/dist/pool.d.ts +1 -1
- package/dist/pool.js +4 -4
- package/dist/resources/index.cjs +4 -4
- package/dist/resources/index.d.cts +2 -2
- package/dist/resources/index.d.ts +2 -2
- package/dist/resources/index.js +7 -7
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/docs/API.md +201 -303
- package/docs/LIVE-TESTING.md +4 -6
- package/docs/N8N-ADAPTER.md +26 -26
- package/n8n-mcp.curation.json +3 -3
- package/package.json +1 -1
package/docs/API.md
CHANGED
|
@@ -260,9 +260,9 @@ status → what the vendor does that triggers it.
|
|
|
260
260
|
| `QueryTooLongError` | `QUERY_TOO_LONG` | policy | — | caller-requested `via: 'get'` whose serialized URL would exceed the 2048-char GET cap — the SDK never silently degrades to POST |
|
|
261
261
|
| `StaleObjectError` | `STALE_OBJECT` | conflict | — | the caller-supplied `lastModifiedDate` no longer matches the vendor's current value (no `If-Match`/ETag exists) |
|
|
262
262
|
| `ConfirmationRequiredError` | `CONFIRMATION_REQUIRED` | policy | — | a destructive / approval-gated op invoked without its exact confirmation flag (`invoke` governor, section 7) |
|
|
263
|
-
| `DeleteBoundaryError` | `DELETE_BOUNDARY` | policy | — |
|
|
264
|
-
| `MarkerUnsafeIdError` | `MARKER_UNSAFE_ID` | policy | — | a
|
|
265
|
-
| `RecoveryTruncatedError` | `RECOVERY_TRUNCATED` | policy | — | the
|
|
263
|
+
| `DeleteBoundaryError` | `DELETE_BOUNDARY` | policy | — | a delete on a resource the vendored spec defines no DELETE for — refused PRE-SEARCH (zero wire; the boundary is the spec-derived plan audit, never a raw vendor 405; issue #9). The throwing op (`operations.recovery`) was deleted in #289 — the refusal code stays in the write-outcome taxonomy |
|
|
264
|
+
| `MarkerUnsafeIdError` | `MARKER_UNSAFE_ID` | policy | — | a marker-filtered match whose id is beyond `Number.MAX_SAFE_INTEGER` (unaddressable through the typed delete surface) — refused with the typed code, never a silent skip (issue #9, P1; the throwing op was deleted in #289 — the refusal code stays) |
|
|
265
|
+
| `RecoveryTruncatedError` | `RECOVERY_TRUNCATED` | policy | — | the marker-filtered bounded search tripped its match cap (more matches than `limit`) — refused instead of a silent truncation / silent empty (issue #9; the throwing op was deleted in #289 — the refusal code stays) |
|
|
266
266
|
| `AutotaskError` | `BUDGET_EXCEEDED` | policy | — (the call that would exceed it is not sent) | `operations.revenueRollup`: the pre-walk estimate exceeds `maxVendorCalls` (refused before any stream; `details: { phase: 'estimate', estimate, budget, suggestions }`) or the counting wrapper reached the budget mid-walk (`phase: 'walk'`; the partial fold is discarded) — `suggestedAction` names the narrowing (issue #234) |
|
|
267
267
|
|
|
268
268
|
Retryable by contract: `NETWORK_ERROR`, `TIMEOUT`, `RATE_LIMITED`,
|
|
@@ -287,8 +287,7 @@ retryability: `emptyReason` (the pinned typed empty-reason — the miss's
|
|
|
287
287
|
kind + note + authority + remedy; §11.10) and `pointerNote` (the
|
|
288
288
|
known-unusable-op pointer sentence — the status/code are untouched, the
|
|
289
289
|
note routes to the operation that works), plus the additive refusal /
|
|
290
|
-
drift payloads (the
|
|
291
|
-
shape-drift marker info). A non-pinned surface gets no `details` at all
|
|
290
|
+
drift payloads (the #13 shape-drift `candidateIds` refusal). A non-pinned surface gets no `details` at all
|
|
292
291
|
(no speculation). On the MCP tools' served error payload, `details` is
|
|
293
292
|
passed through verbatim (live-verified 2026-10-04: the W-D3 pointer note
|
|
294
293
|
rides a real `time_entries.getPosted` vendor 500 on the wire; that 500 is gone since 0.32.0, the pointer-note mechanism is unchanged) and the
|
|
@@ -1132,101 +1131,44 @@ be cross-mode-refused.
|
|
|
1132
1131
|
all 1281 ops × 3 modes, zero wire).
|
|
1133
1132
|
|
|
1134
1133
|
<!-- BEGIN GENERATED:approval-policy -->
|
|
1135
|
-
**Harness approval policy (recommended).** Require approval on every tool whose MCP annotation is `readOnlyHint !== true` — today exactly **
|
|
1134
|
+
**Harness approval policy (recommended).** Require approval on every tool whose MCP annotation is `readOnlyHint !== true` — today exactly **246 tools**: the 244 dedicated write/delete tools (188 `effect=write` + 56 `effect=destructive`, every one of them has a dedicated tool) + `autotask_write` + `autotask_delete`. The remaining **599 tools** (the 586 dedicated read tools + the 10 core read tools + `autotask_read` + `autotask_catalog` + `autotask_describe`) all carry `readOnlyHint: true` and are safe ungated: every op they can route is `effect=read` (zero mutating requests). Refined class for the highest-risk ops: the **57 tools with `destructiveHint: true`** (56 dedicated delete tools + `autotask_delete`) additionally require `confirm === <operation key>` even inside `autotask_delete`. The approval-gated writes (`configuration_items.moveConfigurationItem`, `contacts.moveToCompany`) require the same confirm inside `autotask_write`. Migration: `autotask_invoke` is REPLACED by `autotask_read`/`autotask_write`/`autotask_delete` — update harness config and remove any `autotask_invoke` entry. The rule is derivable from the registry `effect` column and the mode tools’ fixed annotations; it never needs a hand-maintained name list.
|
|
1136
1135
|
<!-- END GENERATED:approval-policy -->
|
|
1137
1136
|
|
|
1138
|
-
### 7.5
|
|
1137
|
+
### 7.5 the `create` dry-run delete-boundary warning (issue #9)
|
|
1139
1138
|
|
|
1140
1139
|
The delete-boundary reality (§11.1, §11.9): the vendored spec defines NO
|
|
1141
1140
|
DELETE on many entity paths — most of them the campaign's own resources. A
|
|
1142
1141
|
`create` that cannot be undone through the API leaves the created record as
|
|
1143
|
-
permanent residue once its id is lost (P1).
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1142
|
+
permanent residue once its id is lost (P1). The one-call recovery op that
|
|
1143
|
+
turned the boundary into a typed, bounded path (the marker-filtered find +
|
|
1144
|
+
confirm-gated delete that shared the SAME generated boundary note) was
|
|
1145
|
+
deleted in #289 — no server consumer; the boundary data, the typed error
|
|
1146
|
+
codes and the dry-run warning remain (§11.9).
|
|
1147
1147
|
|
|
1148
|
-
|
|
1149
|
-
import { recovery } from 'node-autotask/operations';
|
|
1150
|
-
// find the scratch record a write just stamped (ONE bounded query, zero mutating wire):
|
|
1151
|
-
const r = await recovery(client, { resource: 'companies', marker: 'FULL100W-1791110339' });
|
|
1152
|
-
// r.delete.available === false + r.cleanup.note — the spec says the vendor has no DELETE.
|
|
1153
|
-
// where the vendor DOES allow it (delete available):
|
|
1154
|
-
const dry = await recovery(client, { resource: 'configuration_item_dns_records', marker: 'm', field: 'data', parentId: 42, action: 'delete' });
|
|
1155
|
-
const applied = await recovery(client, { resource: 'configuration_item_dns_records', marker: 'm', field: 'data', parentId: 42, action: 'delete' },
|
|
1156
|
-
{ dryRun: false, confirm: 'operations.recovery' });
|
|
1157
|
-
```
|
|
1158
|
-
|
|
1159
|
-
- **`find`** (the default — a read): ONE bounded marker-filtered query
|
|
1160
|
-
(server-side pagination; default `limit` 25, max 100) returns the matched
|
|
1161
|
-
records' ids (`matches: [{ id, value }]`) + the delete-boundary fact
|
|
1162
|
-
(`delete: { available, reason, authority }`) + the UI-only `cleanup` note.
|
|
1163
|
-
Zero mutating wire.
|
|
1164
|
-
- **`delete`** (destructive, dry-run-first): the dry run performs the search
|
|
1165
|
-
and reports the match set with **zero DELETE wire** (`simulated: true`);
|
|
1166
|
-
the apply requires the exact confirm string (`confirm ===
|
|
1167
|
-
'operations.recovery'` — the governor forwards the caller's `confirm`
|
|
1168
|
-
through the trailing bag) and issues one DELETE per safe match id + one
|
|
1169
|
-
verification read per id (`deleted: [{ id, verified, code? }]`).
|
|
1170
|
-
- **the marker field**: the `contains` filter runs on the name-like field
|
|
1171
|
-
the write stamped — default the plan's `markerField` for the resource
|
|
1172
|
-
(the spec-derived deterministic priority over the vendored model's field
|
|
1173
|
-
set), pass `field` to override; REQUIRED when the resource has no
|
|
1174
|
-
`markerField` (a `CONFIG_ERROR` names the candidates).
|
|
1175
|
-
- **P1 discipline — never a silent failure mode**:
|
|
1176
|
-
- a delete on a delete-less resource refuses **PRE-SEARCH** with the typed
|
|
1177
|
-
`DELETE_BOUNDARY` (zero wire — the boundary is the local spec audit,
|
|
1178
|
-
never a raw vendor 405);
|
|
1179
|
-
- a match whose id is beyond `Number.MAX_SAFE_INTEGER` is never a silent
|
|
1180
|
-
skip: `delete` refuses with the typed `MARKER_UNSAFE_ID` (zero DELETE
|
|
1181
|
-
wire); `find` marks the match `id: null` + `unsafeId: true`;
|
|
1182
|
-
- a cap-tripped search (more matches than `limit`) refuses with the typed
|
|
1183
|
-
`RECOVERY_TRUNCATED` — never a silent truncation, never a silent empty;
|
|
1184
|
-
- an unknown `resource` refuses with the typed `RESOURCE_UNKNOWN` (the
|
|
1185
|
-
same code `operations.relationshipMap` uses: carries the resource count
|
|
1186
|
-
+ the `autotask_catalog` pointer).
|
|
1187
|
-
- **the create dry run warns**: on a delete-less resource,
|
|
1148
|
+
- **The create dry run warns**: on a delete-less resource,
|
|
1188
1149
|
`client.<resource>.create(data, { dryRun: true })` carries the
|
|
1189
1150
|
report-only `delete-boundary` check (`ok: true` — `wouldApply` is
|
|
1190
1151
|
preserved) + the boundary note in `warnings[]` (the SAME generated note
|
|
1191
|
-
|
|
1192
|
-
Pinned by `test/
|
|
1193
|
-
governor suite (`test/operations/invoke/modes.test.ts`).
|
|
1152
|
+
the describe channel carries — the describe and dry-run channels cannot
|
|
1153
|
+
drift). Pinned by `test/resources/mutation-safety.test.ts` + the
|
|
1154
|
+
full-surface governor suite (`test/operations/invoke/modes.test.ts`).
|
|
1194
1155
|
|
|
1195
|
-
### 7.6 `
|
|
1156
|
+
### 7.6 `fetchByIds(client, items, opts?)` — bulk fetch by ids
|
|
1196
1157
|
|
|
1197
|
-
|
|
1158
|
+
The cross-resource bulk read with per-item typed results — one entry per
|
|
1198
1159
|
input occurrence: `{ resource, id, found, value, error?: { code, httpStatus?,
|
|
1199
1160
|
message } }`; a NOT_FOUND or fault on one item never aborts the rest; the
|
|
1200
|
-
result is `{ results, ok, failed }`.
|
|
1161
|
+
result is `{ results, ok, failed }`. The op refuses pre-request (zero wire):
|
|
1201
1162
|
non-numeric string ids (pointing at `operations.resolveAny` — canonical
|
|
1202
1163
|
integer strings above 2^53 pass with their exact digits), parent-scoped
|
|
1203
1164
|
children (their `get` needs `parentId`), unknown `fields` keys (the complete
|
|
1204
1165
|
problem list in one message), unsafe number ids (the canonical-string
|
|
1205
1166
|
pointer). The op never fabricates rows: values come only from a vendor 2xx
|
|
1206
1167
|
(query row or GET record); a miss is a typed outcome, never a null-padded
|
|
1207
|
-
row.
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
one sequential GET per item. Per-item `fields?: string[]` is a client-side
|
|
1212
|
-
projection (the GET returns the full model; projection is post-fetch, zero
|
|
1213
|
-
extra wire).
|
|
1214
|
-
**Live field validation (opt-in `opts.liveFields`, issue #66)**: when
|
|
1215
|
-
`true`, each item's `fields` validate against the tenant's **live** entity
|
|
1216
|
-
field metadata (the existing cached metadata channel — with
|
|
1217
|
-
`fieldCache` configured a warm cache costs zero wire; a cold run is one
|
|
1218
|
-
`entityInformation/fields` GET per DISTINCT resource in the batch) instead
|
|
1219
|
-
of the vendored-spec field set alone. A resource whose metadata is
|
|
1220
|
-
unavailable (network fault, vendor 5xx) falls back to the vendored-spec
|
|
1221
|
-
set for THAT resource only — unavailability never throws and never blocks
|
|
1222
|
-
the batch. When available the live set is authoritative: a field it lacks
|
|
1223
|
-
is refused even if the spec lists it, and a live field the spec lacks is
|
|
1224
|
-
accepted — a tenant whose live TimeEntry entity exposes `postedDate` (the
|
|
1225
|
-
live record carries it even when the vendored model omits it) may project
|
|
1226
|
-
it once `liveFields: true`; the default spec-only path still refuses it.
|
|
1227
|
-
The default (no `liveFields`) is today's vendored-spec-only validation,
|
|
1228
|
-
bit-for-bit, zero metadata wire. Field discovery in both modes:
|
|
1229
|
-
`operations.fieldCatalog`.
|
|
1168
|
+
row. The per-item result shapes and the pre-request validation taxonomy are
|
|
1169
|
+
reused from the deleted `fetchMany` op (issue #289 — the shared `FetchMany*`
|
|
1170
|
+
result types kept their names).
|
|
1171
|
+
|
|
1230
1172
|
- **`fetchByIds`** — up to 500 items (mixed resources; the pinned
|
|
1231
1173
|
`FETCH_BY_IDS_MAX_ITEMS`; `opts.maxItems` may lower the cap, never raise
|
|
1232
1174
|
it; over → `CONFIG_ERROR` pre-request, zero wire). **Query-first**: per
|
|
@@ -1241,8 +1183,7 @@ row.
|
|
|
1241
1183
|
limited to the plan-pinned blind surfaces). Wire cost: per resource
|
|
1242
1184
|
`ceil(uniqueIds/250)` query calls + one `get` per query-missed id — a
|
|
1243
1185
|
clean 500-id single-resource batch is **2 wire** (down from 500 sequential
|
|
1244
|
-
GETs); the blind-surface worst case is 2 + up to 500 gets
|
|
1245
|
-
parity, never worse). A chunk-level vendor fault faults every item of that
|
|
1186
|
+
GETs); the blind-surface worst case is 2 + up to 500 gets. A chunk-level vendor fault faults every item of that
|
|
1246
1187
|
chunk (the chunk's `code`/`httpStatus`) and the remaining chunks still run
|
|
1247
1188
|
(input order is preserved across chunks and groups; a duplicate id gets its
|
|
1248
1189
|
own entry with the identical outcome over one wire fetch).
|
|
@@ -1469,14 +1410,17 @@ notes cannot be deleted, so they never use create-then-delete.
|
|
|
1469
1410
|
|
|
1470
1411
|
For per-ticket status / assignee / company use `operations.fetchByIds` on the group ticket ids
|
|
1471
1412
|
(with `names: true`: `statusLabel`, `assignedResourceIDName`, `companyIDName`).
|
|
1472
|
-
- **`
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
behind UTC, e.g. `America/Los_Angeles`, selected the NEXT UTC day; live-verified 2026-10-23.)
|
|
1413
|
+
- **`dateWorked` windows are whole UTC days (issue #66; consolidated in #290)**: the vendor stores
|
|
1414
|
+
`dateWorked` as a date-only value at 00:00 UTC, so the wire bounds are always whole UTC days.
|
|
1415
|
+
`time_entries.dateWorkedWindow` takes `from`/`to` as `YYYY-MM-DD` and the days ARE the bounds — the
|
|
1416
|
+
`tz` option is gone with the consolidation (before it, `tz` was validated but never shifted these
|
|
1417
|
+
bounds), and the relative windows (`today`, `last7`, `lastFullMonth`, ...) are cut. (Before 0.24.0 a
|
|
1418
|
+
`tz` behind UTC, e.g. `America/Los_Angeles`, selected the NEXT UTC day; live-verified 2026-10-23.)
|
|
1419
|
+
|
|
1420
|
+
### 7.12 `timeEntries.approve` — time-entry approval levels (2026-10-26; cut in #292)
|
|
1478
1421
|
|
|
1479
|
-
|
|
1422
|
+
**Cut in #292** (confirmed unused write op — the CHANGELOG names the raw-composition
|
|
1423
|
+
alternative). The vendor facts below stand for anyone composing it by hand:
|
|
1480
1424
|
|
|
1481
1425
|
`timeEntries.approve(id, opts?)` records one approval level for a time entry.
|
|
1482
1426
|
The vendor keeps approvals in a separate entity (`BillingItemApprovalLevels`)
|
|
@@ -1608,9 +1552,8 @@ over a window, by account vertical": the registry op `operations.revenueRollup`
|
|
|
1608
1552
|
label }`, absent otherwise). A label shared by several live picklist ids
|
|
1609
1553
|
selects every one of them.
|
|
1610
1554
|
- **`groupBy`** (default `['vertical', 'contractType']`): `vertical`
|
|
1611
|
-
(`companies.marketSegmentID`), `contractType`, `
|
|
1612
|
-
|
|
1613
|
-
dimensions (#235):
|
|
1555
|
+
(`companies.marketSegmentID`), `contractType`, `company`, `month` (the
|
|
1556
|
+
invoice month), and the join dimensions (#235):
|
|
1614
1557
|
- `contract` — `{ id, name }` (`contractName` joins the contract fetch).
|
|
1615
1558
|
- `lineOfBusiness` — per billing line (`organizationalLevelAssociationID`;
|
|
1616
1559
|
no contract-pointer fallback): three batch `id in` fetches (associations,
|
|
@@ -1621,26 +1564,18 @@ over a window, by account vertical": the registry op `operations.revenueRollup`
|
|
|
1621
1564
|
their amount (LOB figures are floors). A row's `contracts` is "contracts
|
|
1622
1565
|
with at least one line in this LOB", so per-LOB counts can sum to more than
|
|
1623
1566
|
`totals.contracts`.
|
|
1624
|
-
- `resource` — labour lines only (`billingItemType` Labor / Labor
|
|
1625
|
-
Adjustment, or the FK fallback): `timeEntryID` → time entry → resource,
|
|
1626
|
-
`{ id: resourceID, name: 'First Last' }`. Non-labour lines, and labour
|
|
1627
|
-
lines with no time entry (e.g. an adjustment), are `{ id: null, label: 'n/a' }`.
|
|
1628
|
-
- `billingCode` — `billingCodeID` → billing code `{ id, name }`; a line
|
|
1629
|
-
without one is `{ id: null, label: 'none' }`.
|
|
1630
1567
|
|
|
1631
1568
|
Each row key is `{ id, label?, name?, division? }`: picklist text in
|
|
1632
|
-
`label`; vendor entity text (untrusted — company, contract
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
its
|
|
1641
|
-
|
|
1642
|
-
the line projection grows by `organizationalLevelAssociationID`,
|
|
1643
|
-
`timeEntryID` / the type fields and `billingCodeID` the same way.
|
|
1569
|
+
`label`; vendor entity text (untrusted — company, contract and LOB level2
|
|
1570
|
+
names) in `name`, and the LOB division (level1 name, also untrusted) in
|
|
1571
|
+
`division`. Missing values get their own row (`unassigned`,
|
|
1572
|
+
`no LOB assigned`); a value the picklist does not serve stays id-only and is
|
|
1573
|
+
listed in `coverage.blindSurfaces`. A join id the `id in` fetch does not
|
|
1574
|
+
return is never guessed: the line goes to
|
|
1575
|
+
`coverage.excluded.unresolvedAssociations` (association or its level1/level2)
|
|
1576
|
+
with its amount. The bucket (and `coverage.lineOfBusiness`) appears only
|
|
1577
|
+
when its dimension is grouped; the line projection grows by
|
|
1578
|
+
`organizationalLevelAssociationID` the same way.
|
|
1644
1579
|
- **Rows** are sorted by revenue descending, then ids; `top` (default 100,
|
|
1645
1580
|
max 100) keeps the head and folds the tail into `rowsTruncated`. `totals`
|
|
1646
1581
|
always cover every row.
|
|
@@ -1767,19 +1702,19 @@ const lob = await revenueRollup(client, { tenantWide: true, dateFrom: '2025-07-0
|
|
|
1767
1702
|
## 8. Resource surface
|
|
1768
1703
|
|
|
1769
1704
|
<!-- BEGIN GENERATED:api-per-entity -->
|
|
1770
|
-
Standard methods (every resource, `BaseResource`): `query` (stream, 1000-page guarded), `queryBounded` (server-side pagination — one call walks the cursor until the total cap: `limit` (default 500 / max 2500), or independent `walk.maxRows` for exports (pages stay `min(500, limit)`); `hasMore` reports incomplete walks, and explicit walks also carry `truncated`; the MCP `query` surface, routed by `invoke`), `queryAll` (collect → `T[]` — the unbounded raw walk), `count`, `get` (id → record; `item: null` → `NotFoundError`), `create` / `update` / `delete` (where the vendor defines them — mutations take a `MutationOpts` bag: `{ dryRun, lastModifiedDate?, maxRetries? }` (issue #105: `maxRetries` overrides the configured retry budget for every request the mutation issues; `0` = exactly one send per request)), plus the helper floor where the plan declares it: `resolve` (
|
|
1705
|
+
Standard methods (every resource, `BaseResource`): `query` (stream, 1000-page guarded), `queryBounded` (server-side pagination — one call walks the cursor until the total cap: `limit` (default 500 / max 2500), or independent `walk.maxRows` for exports (pages stay `min(500, limit)`); `hasMore` reports incomplete walks, and explicit walks also carry `truncated`; the MCP `query` surface, routed by `invoke`), `queryAll` (collect → `T[]` — the unbounded raw walk), `count`, `get` (id → record; `item: null` → `NotFoundError`), `create` / `update` / `delete` (where the vendor defines them — mutations take a `MutationOpts` bag: `{ dryRun, lastModifiedDate?, maxRetries? }` (issue #105: `maxRetries` overrides the configured retry budget for every request the mutation issues; `0` = exactly one send per request)), plus the helper floor where the plan declares it: `resolve` (122 of 129 — 7 lack it), `search` (41 — bounded contains on the resource text field), `getContext` (5 workflow resources); the `findBy<Field>` methods (findBySerial, findByTicketNumber, findByEmail) are class methods, not plan-declared special ops. Entity metadata on every resource: `getEntityInfo`, `getFieldInfo`, `getUdfInfo` (UDF-capable 16). Parent-scoped children take `(parentId, id)` on the CRUD methods and require `parentId` on `createIfNotExists` (the scoped create POSTs the scoped collection; the dedup query stays global); `query`/`count`/`resolve`/`search` stay global (narrow with a `parentField` filter). Scoped `update` PATCHes the collection (the vendor keys the record by the id in the body).
|
|
1771
1706
|
|
|
1772
1707
|
| resource key | apiEntity | kind | tier | parent | standard ops present | special ops | mcpTool |
|
|
1773
1708
|
|--------------|-----------|------|------|--------|----------------------|-------------|---------|
|
|
1774
1709
|
| `appointments` | Appointment | entity | extended | no | query, count, get, create, update, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_appointment_get |
|
|
1775
1710
|
| `billing_codes` | BillingCode | entity | extended | no | query, count, get, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_billingCode_get |
|
|
1776
1711
|
| `billing_item_approval_levels` | BillingItemApprovalLevel | entity | extended | no | query, count, get, create | — | autotask_billingItemApprovalLevel_get |
|
|
1777
|
-
| `billing_items` | BillingItems | entity | extended | no | query, count, get, update, resolve |
|
|
1778
|
-
| `change_request_links` | ChangeRequestLink | entity | extended | no | query, count, get, create, delete, resolve |
|
|
1712
|
+
| `billing_items` | BillingItems | entity | extended | no | query, count, get, update, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_billingItems_get |
|
|
1713
|
+
| `change_request_links` | ChangeRequestLink | entity | extended | no | query, count, get, create, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_changeRequestLinks_get |
|
|
1779
1714
|
| `checklist_libraries` | ChecklistLibrary | entity | extended | no | query, count, get, create, update, delete, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_checklistLibrary_get |
|
|
1780
1715
|
| `checklist_library_checklist_items` | ChecklistLibraryChecklistItem | entity | extended | yes (checklist_libraries) | query, count, get, create, update, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_checklistLibraryChecklistItem_get |
|
|
1781
1716
|
| `classification_icons` | ClassificationIcon | entity | advanced | no | query, count, get, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_classificationIcon_get |
|
|
1782
|
-
| `companies` | Company | entity | core | no | query, count, get, create, update, resolve, search, getContext | fullProfile, getEntityInfo, getFieldInfo, getUdfInfo, prepareCrmWrite, searchByDomain, searchByIdentity, unbilledWork
|
|
1717
|
+
| `companies` | Company | entity | core | no | query, count, get, create, update, resolve, search, getContext | fullProfile, getEntityInfo, getFieldInfo, getUdfInfo, prepareCrmWrite, searchByDomain, searchByIdentity, unbilledWork | autotask_company_get |
|
|
1783
1718
|
| `company_alerts` | CompanyAlert | entity | extended | yes (companies) | query, count, get, create, update, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_companyAlert_get |
|
|
1784
1719
|
| `company_locations` | CompanyLocation | entity | extended | yes (companies) | query, count, get, create, update, delete, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_companyLocation_get |
|
|
1785
1720
|
| `company_notes` | CompanyNote | entity | extended | yes (companies) | query, count, get, create, update, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_companyNote_get |
|
|
@@ -1794,15 +1729,15 @@ Standard methods (every resource, `BaseResource`): `query` (stream, 1000-page gu
|
|
|
1794
1729
|
| `configuration_item_ssl_subject_alternative_names` | ConfigurationItemSslSubjectAlternativeName | entity | extended | yes (configuration_items) | query, count, get, update, delete, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_configurationItemSslSubjectAlternativeName_get |
|
|
1795
1730
|
| `configuration_item_types` | ConfigurationItemType | entity | extended | no | query, count, get, create, update, delete, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_configurationItemTypes_get |
|
|
1796
1731
|
| `configuration_item_webhooks` | ConfigurationItemWebhook | entity | extended | yes (configuration_items) | query, count, get, create, update, delete, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_configurationItemWebhook_get |
|
|
1797
|
-
| `configuration_items` | ConfigurationItem | entity | core | no | query, count, get, create, update, resolve, search, getContext |
|
|
1732
|
+
| `configuration_items` | ConfigurationItem | entity | core | no | query, count, get, create, update, resolve, search, getContext | findBySerial, getEntityInfo, getFieldInfo, getUdfInfo, moveConfigurationItem, profile | autotask_configurationItems_get |
|
|
1798
1733
|
| `contact_group_contacts` | ContactGroupContacts | entity | extended | no | query, count, get, create, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_contactGroupContacts_get |
|
|
1799
1734
|
| `contact_groups` | ContactGroups | entity | extended | no | query, count, get, create, update, delete, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_contactGroups_get |
|
|
1800
1735
|
| `contact_webhooks` | ContactWebhook | entity | extended | yes (contacts) | query, count, get, create, update, delete, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_contactWebhook_get |
|
|
1801
|
-
| `contacts` | Contact | entity | core | yes (companies) | query, count, get, create, update, delete, resolve, search, getContext | findByEmail, getEntityInfo, getFieldInfo, getUdfInfo, moveToCompany
|
|
1736
|
+
| `contacts` | Contact | entity | core | yes (companies) | query, count, get, create, update, delete, resolve, search, getContext | findByEmail, fullProfile, getEntityInfo, getFieldInfo, getUdfInfo, moveToCompany | autotask_contact_get |
|
|
1802
1737
|
| `contract_billing_rules` | ContractBillingRule | entity | extended | yes (contracts) | query, count, get, create, update, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_contractBillingRule_get |
|
|
1803
1738
|
| `contract_block_hour_factors` | ContractBlockHourFactor | entity | extended | yes (contracts) | query, count, get, create, update, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_contractBlockHourFactor_get |
|
|
1804
1739
|
| `contract_blocks` | ContractBlock | entity | extended | yes (contracts) | query, count, get, create, update, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_contractBlock_get |
|
|
1805
|
-
| `contract_charges` | ContractCharge | entity | extended | yes (contracts) | query, count, get, create, update, delete, resolve, search |
|
|
1740
|
+
| `contract_charges` | ContractCharge | entity | extended | yes (contracts) | query, count, get, create, update, delete, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_contractCharge_get |
|
|
1806
1741
|
| `contract_exclusion_billing_codes` | ContractExclusionBillingCode | entity | extended | yes (contracts) | query, count, get, create, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_contractExclusionBillingCode_get |
|
|
1807
1742
|
| `contract_exclusion_roles` | ContractExclusionRoles | entity | extended | yes (contracts) | query, count, get, create, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_contractExclusionRoles_get |
|
|
1808
1743
|
| `contract_exclusion_set_excluded_roles` | ContractExclusionSetExcludedRoles | entity | extended | yes (contract_exclusion_sets) | query, count, get, create, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_contractExclusionSetExcludedRole_get |
|
|
@@ -1818,22 +1753,22 @@ Standard methods (every resource, `BaseResource`): `query` (stream, 1000-page gu
|
|
|
1818
1753
|
| `contract_service_bundle_units` | ContractServiceBundleUnit | entity | extended | yes (contracts) | query, count, get, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_contractServiceBundleUnit_get |
|
|
1819
1754
|
| `contract_service_bundles` | ContractServiceBundle | entity | extended | yes (contracts) | query, count, get, create, update, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_contractServiceBundle_get |
|
|
1820
1755
|
| `contract_service_units` | ContractServiceUnit | entity | extended | yes (contracts) | query, count, get, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_contractServiceUnit_get |
|
|
1821
|
-
| `contract_services` | ContractService | entity | extended | yes (contracts) | query, count, get, create, update, resolve |
|
|
1756
|
+
| `contract_services` | ContractService | entity | extended | yes (contracts) | query, count, get, create, update, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_contractService_get |
|
|
1822
1757
|
| `contract_ticket_purchases` | ContractTicketPurchases | entity | extended | yes (contracts) | query, count, get, create, update, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_contractTicketPurchase_get |
|
|
1823
|
-
| `contracts` | Contract | entity | extended | no | query, count, get, create, update, resolve, search |
|
|
1758
|
+
| `contracts` | Contract | entity | extended | no | query, count, get, create, update, resolve, search | fullProfile, getEntityInfo, getFieldInfo, getUdfInfo, lineOfBusiness, profitability | autotask_contract_get |
|
|
1824
1759
|
| `countries` | Country | entity | advanced | no | query, count, get, update, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_country_get |
|
|
1825
1760
|
| `currencies` | Currency | entity | advanced | no | query, count, get, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_currency_get |
|
|
1826
1761
|
| `departments` | Department | entity | extended | no | query, count, get, create, update, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_department_get |
|
|
1827
1762
|
| `domain_registrars` | DomainRegistrar | entity | extended | no | query, count, get, create, update, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_DomainRegistrar_get |
|
|
1828
1763
|
| `expense_item_attachments` | ExpenseItemAttachment | entity | extended | yes (expense_items) | query, count, get, create, delete, resolve | download, getEntityInfo, getFieldInfo, getUdfInfo | autotask_expenseItemAttachment_get |
|
|
1829
|
-
| `expense_items` | ExpenseItem | entity | extended | yes (expense_reports) | query, count, get, create, update, resolve |
|
|
1764
|
+
| `expense_items` | ExpenseItem | entity | extended | yes (expense_reports) | query, count, get, create, update, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_expenseItem_get |
|
|
1830
1765
|
| `expense_reports` | ExpenseReport | entity | extended | no | query, count, get, create, update, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_expenseReport_get |
|
|
1831
|
-
| `holiday_sets` | HolidaySet | entity | advanced | no | query, count, get, create, update, delete, resolve |
|
|
1832
|
-
| `holidays` | Holiday | entity | advanced | yes (holiday_sets) | query, count, get, create, update, delete, resolve |
|
|
1766
|
+
| `holiday_sets` | HolidaySet | entity | advanced | no | query, count, get, create, update, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_holidaySet_get |
|
|
1767
|
+
| `holidays` | Holiday | entity | advanced | yes (holiday_sets) | query, count, get, create, update, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_holiday_get |
|
|
1833
1768
|
| `invoice_templates` | InvoiceTemplate | entity | advanced | no | query, count, get | getEntityInfo, getFieldInfo, getUdfInfo | autotask_invoiceTemplate_get |
|
|
1834
|
-
| `invoices` | Invoice | entity | extended | no | query, count, get, update, resolve |
|
|
1769
|
+
| `invoices` | Invoice | entity | extended | no | query, count, get, update, resolve | getEntityInfo, getFieldInfo, getUdfInfo, markupHtml, markupXml, pdf | autotask_invoice_get |
|
|
1835
1770
|
| `notification_history` | NotificationHistory | entity | advanced | no | query, count, get, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_notificationHistory_get |
|
|
1836
|
-
| `opportunities` | Opportunity | entity | extended | no | query, count, get, create, update, resolve |
|
|
1771
|
+
| `opportunities` | Opportunity | entity | extended | no | query, count, get, create, update, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_opportunity_get |
|
|
1837
1772
|
| `opportunity_attachments` | OpportunityAttachment | entity | extended | yes (opportunities) | query, count, get, create, delete, resolve | download, getEntityInfo, getFieldInfo, getUdfInfo | autotask_opportunityAttachment_get |
|
|
1838
1773
|
| `opportunity_categories` | OpportunityCategory | entity | extended | no | query, count, get, update, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_opportunityCategories_get |
|
|
1839
1774
|
| `organizational_level1s` | OrganizationalLevel1 | entity | advanced | no | query, count, get, create, update | getEntityInfo, getFieldInfo, getUdfInfo | autotask_organizationalLevel1_get |
|
|
@@ -1841,7 +1776,7 @@ Standard methods (every resource, `BaseResource`): `query` (stream, 1000-page gu
|
|
|
1841
1776
|
| `organizational_level_associations` | OrganizationalLevelAssociation | entity | advanced | no | query, count, get, create, update | getEntityInfo, getFieldInfo, getUdfInfo | autotask_organizationalLevelAssociation_get |
|
|
1842
1777
|
| `product_vendors` | ProductVendors | entity | extended | yes (products) | query, count, get, create, update, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_productVendor_get |
|
|
1843
1778
|
| `products` | Product | entity | extended | no | query, count, get, create, update, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_product_get |
|
|
1844
|
-
| `project_charges` | ProjectCharge | entity | extended | yes (projects) | query, count, get, create, update, delete, resolve, search |
|
|
1779
|
+
| `project_charges` | ProjectCharge | entity | extended | yes (projects) | query, count, get, create, update, delete, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_projectCharge_get |
|
|
1845
1780
|
| `project_notes` | ProjectNote | entity | extended | yes (projects) | query, count, get, create, update, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_projectNote_get |
|
|
1846
1781
|
| `phases` | Phase | entity | extended | yes (projects) | query, count, get, create, update, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_phase_get |
|
|
1847
1782
|
| `tasks` | Task | entity | extended | yes (projects) | query, count, get, create, update, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_task_get |
|
|
@@ -1853,21 +1788,21 @@ Standard methods (every resource, `BaseResource`): `query` (stream, 1000-page gu
|
|
|
1853
1788
|
| `quote_items` | QuoteItem | entity | extended | yes (quotes) | query, count, get, create, update, delete, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_quoteItem_get |
|
|
1854
1789
|
| `quote_locations` | QuoteLocation | entity | extended | yes (quotes) | query, count, get, create, update, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_quoteLocation_get |
|
|
1855
1790
|
| `quote_templates` | QuoteTemplate | entity | extended | no | query, count, get, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_quoteTemplate_get |
|
|
1856
|
-
| `quotes` | Quote | entity | extended | no | query, count, get, create, update, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_quote_get |
|
|
1791
|
+
| `quotes` | Quote | entity | extended | no | query, count, get, create, update, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo, withItems | autotask_quote_get |
|
|
1857
1792
|
| `resource_role_queues` | ResourceRoleQueue | entity | extended | yes (resources) | query, count, get, create, update, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_resourceRoleQueue_get |
|
|
1858
1793
|
| `resource_roles` | ResourceRole | entity | extended | yes (resources) | query, count, get, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_resourceRole_get |
|
|
1859
1794
|
| `resource_skills` | ResourceSkill | entity | extended | yes (resources) | query, count, get, update, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_resourceSkill_get |
|
|
1860
1795
|
| `resource_time_off_additional` | ResourceTimeOffAdditional | entity | extended | yes (resources) | update, resolve | getByResource, getEntityInfo, getFieldInfo, getUdfInfo | autotask_resourceTimeOffAdditional |
|
|
1861
1796
|
| `resource_time_off_approvers` | ResourceTimeOffApprover | entity | extended | yes (resources) | query, count, get, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_resourceTimeOffApprover_get |
|
|
1862
1797
|
| `resource_time_off_balances` | ResourceTimeOffBalance | entity | extended | yes (resources) | get, resolve | getByResource, getByYear, getEntityInfo, getFieldInfo, getUdfInfo | autotask_resourceTimeOffBalance_get |
|
|
1863
|
-
| `resources` | Resource | entity | core | no | query, count, get, update, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo,
|
|
1798
|
+
| `resources` | Resource | entity | core | no | query, count, get, update, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo, whoAmI, workload | autotask_resource_get |
|
|
1864
1799
|
| `roles` | Role | entity | extended | no | query, count, get, create, update, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_role_get |
|
|
1865
1800
|
| `sales_orders` | SalesOrder | entity | extended | no | query, count, get, update | getEntityInfo, getFieldInfo, getUdfInfo | autotask_salesOrder_get |
|
|
1866
1801
|
| `service_call_task_resources` | ServiceCallTaskResource | entity | extended | yes (service_call_tasks) | query, count, get, create, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_serviceCallTaskResource_get |
|
|
1867
1802
|
| `service_call_tasks` | ServiceCallTask | entity | extended | yes (service_calls) | query, count, get, create, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_serviceCallTask_get |
|
|
1868
1803
|
| `service_call_ticket_resources` | ServiceCallTicketResource | entity | extended | yes (service_call_tickets) | query, count, get, create, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_serviceCallTicketResource_get |
|
|
1869
1804
|
| `service_call_tickets` | ServiceCallTicket | entity | extended | yes (service_calls) | query, count, get, create, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_serviceCallTicket_get |
|
|
1870
|
-
| `service_calls` | ServiceCall | entity | extended | no | query, count, get, create, update, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_serviceCall_get |
|
|
1805
|
+
| `service_calls` | ServiceCall | entity | extended | no | query, count, get, create, update, delete, resolve | fullProfile, getEntityInfo, getFieldInfo, getUdfInfo | autotask_serviceCall_get |
|
|
1871
1806
|
| `service_level_agreement_results` | ServiceLevelAgreementResults | entity | advanced | no | query, count, get, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_serviceLevelAgreementResults_get |
|
|
1872
1807
|
| `services` | Service | entity | extended | no | query, count, get, create, update, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_service_get |
|
|
1873
1808
|
| `skills` | Skill | entity | extended | no | query, count, get, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_skill_get |
|
|
@@ -1880,13 +1815,13 @@ Standard methods (every resource, `BaseResource`): `query` (stream, 1000-page gu
|
|
|
1880
1815
|
| `tags` | Tag | entity | advanced | no | query, count, get, create, update, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_tag_get |
|
|
1881
1816
|
| `task_secondary_resources` | TaskSecondaryResource | entity | extended | yes (tasks) | query, count, get, create, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_taskSecondaryResources_get |
|
|
1882
1817
|
| `tax_regions` | TaxRegion | entity | advanced | no | query, count, get, create, update | getEntityInfo, getFieldInfo, getUdfInfo | autotask_taxRegion_get |
|
|
1883
|
-
| `ticket_additional_configuration_items` | TicketAdditionalConfigurationItem | entity | extended | yes (tickets) | query, count, get, create, delete, resolve |
|
|
1884
|
-
| `ticket_additional_contacts` | TicketAdditionalContact | entity | extended | yes (tickets) | query, count, get, create, delete, resolve |
|
|
1818
|
+
| `ticket_additional_configuration_items` | TicketAdditionalConfigurationItem | entity | extended | yes (tickets) | query, count, get, create, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_ticketAdditionalConfigurationItem_get |
|
|
1819
|
+
| `ticket_additional_contacts` | TicketAdditionalContact | entity | extended | yes (tickets) | query, count, get, create, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_ticketAdditionalContact_get |
|
|
1885
1820
|
| `ticket_attachments` | TicketAttachment | entity | extended | yes (tickets) | query, count, get, create, delete, resolve | download, getEntityInfo, getFieldInfo, getUdfInfo | autotask_ticketAttachment_get |
|
|
1886
1821
|
| `ticket_categories` | TicketCategory | entity | extended | no | query, count, get, update, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_ticketCategories_get |
|
|
1887
1822
|
| `ticket_category_field_defaults` | TicketCategoryFieldDefault | entity | extended | yes (ticket_categories) | query, count, get, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_ticketCategoryFieldDefaults_get |
|
|
1888
1823
|
| `ticket_change_request_approvals` | TicketChangeRequestApproval | entity | extended | yes (tickets) | query, count, get, create, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_ticketChangeRequestApprovals_get |
|
|
1889
|
-
| `ticket_charges` | TicketCharge | entity | extended | yes (tickets) | query, count, get, create, update, delete, resolve, search |
|
|
1824
|
+
| `ticket_charges` | TicketCharge | entity | extended | yes (tickets) | query, count, get, create, update, delete, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo, moveToTicket | autotask_ticketCharge_get |
|
|
1890
1825
|
| `ticket_checklist_items` | TicketChecklistItem | entity | extended | yes (tickets) | query, count, get, create, update, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_ticketChecklistItem_get |
|
|
1891
1826
|
| `ticket_checklist_libraries` | TicketChecklistLibrary | entity | extended | yes (tickets) | create, resolve | attachToDocument, getEntityInfo, getFieldInfo, getUdfInfo | autotask_ticketChecklistLibrary |
|
|
1892
1827
|
| `ticket_history` | TicketHistory | entity | extended | no | query, count, get, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_ticketHistory_get |
|
|
@@ -1895,8 +1830,8 @@ Standard methods (every resource, `BaseResource`): `query` (stream, 1000-page gu
|
|
|
1895
1830
|
| `ticket_notes` | TicketNote | entity | extended | yes (tickets) | query, count, get, create, update, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_ticketNote_get |
|
|
1896
1831
|
| `ticket_secondary_resources` | TicketSecondaryResource | entity | extended | yes (tickets) | query, count, get, create, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo | autotask_ticketSecondaryResources_get |
|
|
1897
1832
|
| `ticket_webhooks` | TicketWebhook | entity | extended | yes (tickets) | query, count, get, create, update, delete, resolve, search | getEntityInfo, getFieldInfo, getUdfInfo | autotask_ticketWebhook_get |
|
|
1898
|
-
| `tickets` | Ticket | entity | core | no | query, count, get, create, update, resolve, search, getContext | changeStatus, findByTicketNumber, forResource, fullContext, getEntityInfo, getFieldInfo, getUdfInfo, lineOfBusiness,
|
|
1899
|
-
| `time_entries` | TimeEntry | entity | extended | no | query, count, get, create, update, delete, resolve |
|
|
1833
|
+
| `tickets` | Ticket | entity | core | no | query, count, get, create, update, resolve, search, getContext | changeStatus, findByTicketNumber, forResource, fullContext, getEntityInfo, getFieldInfo, getUdfInfo, lineOfBusiness, slaHealthCheck, statusSemantics, summary, timeline | autotask_ticket_get |
|
|
1834
|
+
| `time_entries` | TimeEntry | entity | extended | no | query, count, get, create, update, delete, resolve | dateWorkedWindow, getEntityInfo, getFieldInfo, getUdfInfo, moveToTicket | autotask_timeEntry_get |
|
|
1900
1835
|
| `time_entry_attachments` | TimeEntryAttachment | entity | extended | yes (time_entries) | query, count, get, create, delete, resolve | download, getEntityInfo, getFieldInfo, getUdfInfo | autotask_timeEntryAttachment_get |
|
|
1901
1836
|
| `time_off_requests` | TimeOffRequest | entity | extended | no | query, count, get, resolve | approve, getEntityInfo, getFieldInfo, getUdfInfo, reject | autotask_timeOffRequest_get |
|
|
1902
1837
|
| `inventory_products` | InventoryProduct | entity | extended | no | query, count, get, create, update, delete, resolve | getEntityInfo, getFieldInfo, getUdfInfo, stockedItemsAdd, stockedItemsRemove, stockedItemsTransfer | autotask_inventoryProduct_get |
|
|
@@ -1908,7 +1843,7 @@ Standard methods (every resource, `BaseResource`): `query` (stream, 1000-page gu
|
|
|
1908
1843
|
| `threshold_information` | — | integration | core | no | get | — | autotask_threshold_information |
|
|
1909
1844
|
| `zone_information` | — | integration | advanced | no | get | — | autotask_zone_information |
|
|
1910
1845
|
|
|
1911
|
-
|
|
1846
|
+
136 resources (129 entity + 7 integration). Rows are generated from the registry — do not hand-type.
|
|
1912
1847
|
<!-- END GENERATED:api-per-entity -->
|
|
1913
1848
|
|
|
1914
1849
|
**`companies.search` include_family (issue #59).** `companies.search` is
|
|
@@ -1972,15 +1907,14 @@ drift). Records exist only for plan rows that are implemented/tested; the
|
|
|
1972
1907
|
plan (`capabilities.plan.json`) is the single source of truth and the
|
|
1973
1908
|
registry is its compiled form.
|
|
1974
1909
|
|
|
1975
|
-
**
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
`
|
|
1980
|
-
|
|
1910
|
+
**SDK-default window (issues #115/#291).** `contracts.profitability` takes
|
|
1911
|
+
`dateFrom`/`dateTo` as an optional PAIR: both omitted → the window defaults to
|
|
1912
|
+
the last full calendar month (UTC) and the result's `period` carries
|
|
1913
|
+
`defaulted: true` plus a note (month label + UTC basis); exactly one supplied
|
|
1914
|
+
→ a typed `CONFIG_ERROR` before any wire; both supplied → the existing
|
|
1915
|
+
validation. The consumer no longer fills the window — the served
|
|
1981
1916
|
`metadata.args`/`metadata.usage` and the record's
|
|
1982
|
-
`inputSchema.opts.description` state the
|
|
1983
|
-
raw manifest should apply the same contract.
|
|
1917
|
+
`inputSchema.opts.description` state the SDK default.
|
|
1984
1918
|
|
|
1985
1919
|
### 9.1 Generated artifact #5: `src/relationships.ts` (the relationship map + zone facts)
|
|
1986
1920
|
|
|
@@ -2005,11 +1939,11 @@ cannot drift from the plan):
|
|
|
2005
1939
|
describe channel cannot drift.
|
|
2006
1940
|
- `DELETE_BOUNDARY_NOTES` (issue #9) — resource → the spec-derived
|
|
2007
1941
|
delete-boundary note (`plan.deleteBoundaries`): the create dry run's
|
|
2008
|
-
`delete-boundary` warning
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
the model has none).
|
|
1942
|
+
`delete-boundary` warning reads the map (the op that shared it,
|
|
1943
|
+
`operations.recovery`, was deleted in #289 — the dry-run and describe
|
|
1944
|
+
channels cannot drift).
|
|
1945
|
+
- `MARKER_FIELDS` (issue #9) — resource → the spec-derived name-like marker
|
|
1946
|
+
field (the plan's `markerField`; absent when the model has none).
|
|
2013
1947
|
- `EMPTY_REASON_STAMPS` (issue #14) — resource → op → the typed empty-reason
|
|
2014
1948
|
`{ surface, kind, note, authority, remedy }` (`plan.emptyReasons`, the
|
|
2015
1949
|
ww29-sweep-pinned 184 rows): the runtime lookup `base.ts` attaches to
|
|
@@ -2031,10 +1965,10 @@ The per-resource entry also carries `deleteBoundary`
|
|
|
2031
1965
|
|
|
2032
1966
|
Consumers: the runtime sentinel wrap (T10), `operations.relationshipMap`
|
|
2033
1967
|
(S12-B — the `autotask_relationship_map` tool serves `RELATIONSHIP_MAP`
|
|
2034
|
-
zero-wire by construction)
|
|
2035
|
-
`
|
|
2036
|
-
|
|
2037
|
-
|
|
1968
|
+
zero-wire by construction) and the create dry run (§7.5). Drift-guarded:
|
|
1969
|
+
`capabilities:check` fails when the artifact bytes ≠ a fresh re-derivation
|
|
1970
|
+
of the plan (`test/scripts/relationships-artifact.test.ts` pins it
|
|
1971
|
+
TDD-style).
|
|
2038
1972
|
## 10. Response redaction (secret and personal fields)
|
|
2039
1973
|
|
|
2040
1974
|
The SDK boundary redacts response field values. A vendor 2xx JSON body is
|
|
@@ -2119,8 +2053,9 @@ resource paths, of which **40** expose a create op (the stampable set; the
|
|
|
2119
2053
|
13-record live-verified set above stays a distinct channel, `proof:
|
|
2120
2054
|
'live-verified'`). The new `plan.deleteBoundaries` block (spec-derived,
|
|
2121
2055
|
`proof: 'spec-derived'`) stamps each affected create's descriptor
|
|
2122
|
-
(`metadata.deleteBoundary`)
|
|
2123
|
-
|
|
2056
|
+
(`metadata.deleteBoundary`) and the create dry run (§7.5) read the SAME
|
|
2057
|
+
generated note (the one-call recovery op that shared it was deleted in
|
|
2058
|
+
#289) — see §11.9.
|
|
2124
2059
|
|
|
2125
2060
|
### 11.2 Blind count/query surfaces (get is the authority)
|
|
2126
2061
|
|
|
@@ -2166,7 +2101,7 @@ resource paths, of which **40** expose a create op (the stampable set; the
|
|
|
2166
2101
|
type itself is discoverable via `configuration_item_types.query` (the wall is the
|
|
2167
2102
|
FK, not the type).
|
|
2168
2103
|
|
|
2169
|
-
### 11.4 `time_entries.
|
|
2104
|
+
### 11.4 `time_entries.create` — zone facts
|
|
2170
2105
|
|
|
2171
2106
|
- The vendor **double-validates roleID** (an active general allocation code AND
|
|
2172
2107
|
a Role reference); in ww29 the two sets are disjoint, so the task/ticket
|
|
@@ -2299,13 +2234,12 @@ of the data).
|
|
|
2299
2234
|
everything — the sentinel notes, the record stamps, the generated
|
|
2300
2235
|
`src/relationships.ts` (§9.1) and this section.
|
|
2301
2236
|
- **S12 additions (PLANNED — land with the A/B workstreams, not yet on
|
|
2302
|
-
main)**:
|
|
2237
|
+
main)**: new registry ops — `tickets.openWithNoteAndAssign` /
|
|
2303
2238
|
`tickets.changeStatus` / `tickets.fullContext` · `contacts.upsertByEmail` ·
|
|
2304
2239
|
`companies.upsertByName` / `companies.fullProfile` ·
|
|
2305
|
-
`configuration_items.profile` · `operations.
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
coordinator step.
|
|
2240
|
+
`configuration_items.profile` · `operations.relationshipMap` (serves the
|
|
2241
|
+
§9.1 artifact zero-wire). Each gets a curation row in
|
|
2242
|
+
`MCP_TOOL_OVERRIDES.json` at the post-merge coordinator step.
|
|
2309
2243
|
|
|
2310
2244
|
### 11.8 Live-validated create examples + the `createIfNotExists` dedupFields shapes
|
|
2311
2245
|
|
|
@@ -2404,11 +2338,13 @@ treat `entry.evidence` with the same trust you already apply to
|
|
|
2404
2338
|
`vendorEvidence` on this surface (plan-grade provenance, not model-facing
|
|
2405
2339
|
prose). The old `string[]` shape is gone.
|
|
2406
2340
|
|
|
2407
|
-
### 11.9 Delete-boundary awareness
|
|
2341
|
+
### 11.9 Delete-boundary awareness (issue #9)
|
|
2408
2342
|
|
|
2409
2343
|
The §11.1 wall (7 live-verified 405 endpoints) is now a **spec-derived
|
|
2410
2344
|
channel** that covers the whole surface, not just the 13 live-verified
|
|
2411
|
-
records. The
|
|
2345
|
+
records. The one-call recovery op that turned this boundary into a typed,
|
|
2346
|
+
bounded path was deleted in #289 (no server consumer) — the channel below
|
|
2347
|
+
is the plan data + the create dry run that remain. The audit (`scripts/lib/delete-boundary.mjs` — the same shared lib
|
|
2412
2348
|
the S11-F stamp channel and the F2 manifest note use) re-derives the
|
|
2413
2349
|
vendored spec's DELETE-absence set, and the plan carries it as a NEW top-level
|
|
2414
2350
|
block (distinct from `plan.blindSurfaces`, which the 6-pin set-equality gate
|
|
@@ -2424,27 +2360,25 @@ keeps untouched):
|
|
|
2424
2360
|
`autotask_describe` surfaces it before the write).
|
|
2425
2361
|
- **generated runtime data** — `src/relationships.ts` emits
|
|
2426
2362
|
`DELETE_BOUNDARY_NOTES` (resource → note) + `MARKER_FIELDS` (resource → the
|
|
2427
|
-
spec-derived name-like field
|
|
2428
|
-
|
|
2363
|
+
spec-derived name-like marker field, 22 pins) from the same plan (zero
|
|
2364
|
+
wire by construction).
|
|
2429
2365
|
- **the create dry run** (`src/resources/base.ts`) — on a delete-less
|
|
2430
2366
|
resource the envelope gains the report-only `delete-boundary` check
|
|
2431
2367
|
(`ok: true`) + the boundary note in `warnings[]`. The check is
|
|
2432
2368
|
report-only on purpose: the write itself is valid, so `wouldApply` is
|
|
2433
2369
|
preserved (the design's `ok: false` example would have broken the pinned
|
|
2434
2370
|
`companies`/`tickets` create-dry-run contract).
|
|
2435
|
-
-
|
|
2436
|
-
turns the boundary from a wall into a typed, bounded path: find the record
|
|
2437
|
-
a write just created (one bounded marker query), and where the vendor
|
|
2438
|
-
allows it, remove it (dry-run-first, confirm-gated apply, per-id
|
|
2439
|
-
verification). The three new typed errors (`DELETE_BOUNDARY`,
|
|
2371
|
+
- **typed errors** — the three issue-#9 refusal codes (`DELETE_BOUNDARY`,
|
|
2440
2372
|
`MARKER_UNSAFE_ID`, `RECOVERY_TRUNCATED`) + the reused `RESOURCE_UNKNOWN`
|
|
2441
|
-
|
|
2442
|
-
silent
|
|
2443
|
-
|
|
2444
|
-
|
|
2373
|
+
stay in the write-outcome taxonomy (`src/errors/mutation.ts`) after #289
|
|
2374
|
+
deleted the op that threw them: never a raw 405, never a silent skip,
|
|
2375
|
+
never a silent empty.
|
|
2376
|
+
- **the gate** (`scripts/check-capabilities.mjs`) — the channel has its own
|
|
2377
|
+
set-equality clause: the block must equal a fresh spec re-derivation
|
|
2445
2378
|
over the stampable set, and EXACTLY the pinned create records carry
|
|
2446
2379
|
`metadata.deleteBoundary` (content match). Pinned by
|
|
2447
|
-
`test/
|
|
2380
|
+
`test/scripts/check-capabilities.test.ts` (the F5 clauses) + the
|
|
2381
|
+
full-surface governor suite.
|
|
2448
2382
|
|
|
2449
2383
|
### 11.10 Typed empty reasons — `emptyReason` (issue #14)
|
|
2450
2384
|
|
|
@@ -2525,14 +2459,17 @@ blind) return `{ count, emptyReason? }` on a 0 — check
|
|
|
2525
2459
|
existing contract, now with the reason alongside).
|
|
2526
2460
|
|
|
2527
2461
|
|
|
2528
|
-
### 11.11
|
|
2462
|
+
### 11.11 The zero-wire blind-surface + reachability plan data (issue #11)
|
|
2529
2463
|
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
14), so an agent can read "what is blind on
|
|
2533
|
-
budget or guessing
|
|
2464
|
+
The campaign's own discovery data ships as plan data (ZERO vendor calls —
|
|
2465
|
+
`plan.reachability` is derived into `src/relationships.ts`, drift-guarded
|
|
2466
|
+
by `capabilities:check` clause 14), so an agent can read "what is blind on
|
|
2467
|
+
this zone" without burning budget or guessing. The two ops that served it
|
|
2468
|
+
at runtime (`operations.blindSurfaces`, `operations.probeReachability`)
|
|
2469
|
+
were deleted in #289 (no server consumer) — the data and its derivation
|
|
2470
|
+
remain:
|
|
2534
2471
|
|
|
2535
|
-
- **`
|
|
2472
|
+
- **`BLIND_SURFACE_INVENTORY`** — the 124-surface inventory: per surface the
|
|
2536
2473
|
kind (`unowned-blind` / `unfiltered-blind` / `unknown` — the 46
|
|
2537
2474
|
data-with-rows surfaces ride `unknown` by design: the sweep's limit-2 read
|
|
2538
2475
|
proves data, not the absence of it), the recorded `unfilteredRows` (0-2
|
|
@@ -2543,14 +2480,13 @@ budget or guessing:
|
|
|
2543
2480
|
note. Clause 14 pins the row set equal to the plan's query-able resources,
|
|
2544
2481
|
the counts derived from the rows, and the row-level kind invariant (a
|
|
2545
2482
|
blind-class row must have 0 recorded rows).
|
|
2546
|
-
- **`
|
|
2547
|
-
(
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
from a blind surface proves nothing — the inventory's kind says why).
|
|
2483
|
+
- **`REACHABILITY_PROBE_DEFAULTS`** — the derived probe defaults: the hard
|
|
2484
|
+
wire cap (80), the default cap (40), the limit bounds (1-2, default 2)
|
|
2485
|
+
and the default target set — the derived non-data-full rows (the 70
|
|
2486
|
+
surfaces a re-discovery sweep starts from: the 69 blind +
|
|
2487
|
+
`ticket_history`). Derived zero-wire; the gate re-derives the target set,
|
|
2488
|
+
so it cannot drift from the inventory. (A 0 from a blind surface proves
|
|
2489
|
+
nothing — the inventory's kind says why.)
|
|
2554
2490
|
|
|
2555
2491
|
### 11.12 Zone-fact create guidance (issue #12)
|
|
2556
2492
|
|
|
@@ -2697,7 +2633,7 @@ metadata is (the `autotask_describe` channel, `capabilities.json`,
|
|
|
2697
2633
|
key. Unlike `pointerNote`, caveats ride the describe channel only (they are
|
|
2698
2634
|
not attached to error details).
|
|
2699
2635
|
|
|
2700
|
-
### 11.13 Posted state from billing items (W-D3), `
|
|
2636
|
+
### 11.13 Posted state from billing items (W-D3), `dateWorkedWindow`, and drifted-create marker recovery (issues #10/#13/#237)
|
|
2701
2637
|
|
|
2702
2638
|
- **W-D3 (live, ww29, re-verified 2026-10-27)** — this zone's TimeEntry entity
|
|
2703
2639
|
has **no posted-state field**: time-entry reads carry no `postedDate` /
|
|
@@ -2717,52 +2653,39 @@ metadata is (the `autotask_describe` channel, `capabilities.json`,
|
|
|
2717
2653
|
condition is ever sent. The join is one `BillingItems` query per 250 ids;
|
|
2718
2654
|
the entry read is capped at 500, so a call costs at most 1 read + 2 joins
|
|
2719
2655
|
and a window with more than 500 entries returns the first 500 with
|
|
2720
|
-
`
|
|
2656
|
+
`truncated` true and a `nextCursor` to continue from (or use
|
|
2721
2657
|
`countOnly` for the window's totals; see the paging bullet below). A 429 or vendor fault propagates as a typed
|
|
2722
2658
|
error. The API user therefore needs BillingItems read permission; a permission fault propagates as a typed error.
|
|
2723
|
-
- **`time_entries.
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
`
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
`limit`); re-query with a wider window or a narrowing filter to reconcile
|
|
2754
|
-
the totals. `time_entries.dateWorkedWindow(from, to, opts?)` is the
|
|
2755
|
-
unambiguous-named alias: the same wire, the same result (incl. `truncated`),
|
|
2756
|
-
its own op identity (plan row / MCP tool
|
|
2757
|
-
`autotask_timeEntry_date_worked_window` / operation label) — the name
|
|
2758
|
-
names the `dateWorked` field the window is on; `postedWithin` keeps its
|
|
2759
|
-
name and default semantics. Both accept `opts.tz` (an IANA zone, validated)
|
|
2760
|
-
but, since 0.24.0, it never shifts the bounds: `dateWorked` is a date-only
|
|
2761
|
-
value at 00:00 UTC, so `from`/`to` are the days (see §7.11). The same `tz`
|
|
2762
|
-
opt is on every date-window op — see §11.22.
|
|
2763
|
-
- **Paging past 500 entries and whole-window totals (issue #242)** — all four
|
|
2764
|
-
ops (`getPosted`, `getUnposted`, `postedWithin`, `dateWorkedWindow`) take
|
|
2765
|
-
three more options:
|
|
2659
|
+
- **`time_entries.dateWorkedWindow(from, to, opts?)`** — the ONE posted-state read
|
|
2660
|
+
(issue #290; absorbs `getPosted` / `getUnposted` / `postedWithin` and the
|
|
2661
|
+
relative windows). `from`/`to` are required inclusive UTC `dateWorked` days
|
|
2662
|
+
(`YYYY-MM-DD` date strings — the guard forbids Date positionals and refuses
|
|
2663
|
+
impossible days), one bounded read (`limit` 1-500, default 500 — the hard cap,
|
|
2664
|
+
issue #128: a larger window needs `time_entries.query` with the same
|
|
2665
|
+
`dateWorked` filter and a suitable `limit` or explicit `walk.maxRows` cap,
|
|
2666
|
+
section 4) returning
|
|
2667
|
+
`{ items, postedCount, unpostedCount, window, nextCursor, unconfirmed, truncated }`;
|
|
2668
|
+
the counts come from the billing-item join (any billing item referencing the
|
|
2669
|
+
entry) and split every entry read (before the narrowing), so `items.length`
|
|
2670
|
+
can differ from their sum; `opts.state` (`'posted'` / `'unposted'`, omitted =
|
|
2671
|
+
both) narrows `items` client-side after the join, and `ticketID` / `resourceID`
|
|
2672
|
+
narrow the read. The no-window form (a call without dates reading the
|
|
2673
|
+
tenant's first 500 time entries, effectively the oldest) is gone: a read with
|
|
2674
|
+
no `dateWorked` filter goes through `time_entries.query`. Live evidence for
|
|
2675
|
+
the visibility class (unfiltered query 0 rows, windowed read has data) from
|
|
2676
|
+
2026-10-06 still stands; the posted/unposted split from that period does not.
|
|
2677
|
+
- **`truncated` (issue #66, consolidated in #290)** — `truncated` is true when
|
|
2678
|
+
the bounded read came back with a live `pageDetails.nextPageUrl` (more
|
|
2679
|
+
matching rows exist; `requestCount` is the MaxRecords echo, not a total, so
|
|
2680
|
+
it cannot carry this signal) or a join page was truncated — the
|
|
2681
|
+
posted/unposted counts then cover the returned rows ONLY (at most `limit`);
|
|
2682
|
+
re-query with a wider window or a narrowing filter to reconcile the totals.
|
|
2683
|
+
The `tz` option is gone with the consolidation (`dateWorked` is a date-only
|
|
2684
|
+
value at 00:00 UTC — the `from`/`to` days ARE the days, never shifted; see
|
|
2685
|
+
§7.11), and the `postedWithin` / `dateWorkedWindow` alias pair is one op now
|
|
2686
|
+
(plan row / MCP tool `autotask_timeEntry_date_worked_window`).
|
|
2687
|
+
- **Paging past 500 entries and whole-window totals (issue #242)** —
|
|
2688
|
+
`dateWorkedWindow` takes three more options:
|
|
2766
2689
|
- `cursor?: string` — every rows result now carries `nextCursor: string | null`
|
|
2767
2690
|
(the vendor's next-page cursor for the entry read). Pass it back as
|
|
2768
2691
|
`opts.cursor` with the **same** window and filters to read the next page;
|
|
@@ -2773,12 +2696,11 @@ metadata is (the `autotask_describe` channel, `capabilities.json`,
|
|
|
2773
2696
|
continuation (`/V1.0/TimeEntries/query/next`), or a cursor longer than
|
|
2774
2697
|
2,048 characters, is refused (`RESPONSE_REJECTED`) before any request. Every rows result also carries
|
|
2775
2698
|
`unconfirmed: number`: the entries on this page whose billing-item join
|
|
2776
|
-
page was truncated. They are withheld from `items`
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
above 0 `hasMore` / `truncated` is also true. Sum it across a loop to know
|
|
2699
|
+
page was truncated. They are withheld from a `state`-narrowed `items`
|
|
2700
|
+
(with `state` omitted they stay in `items`, unclassified) and from both
|
|
2701
|
+
counts, so a cursor loop does not see them as posted or unposted;
|
|
2702
|
+
`unconfirmed` is 0 normally, and when it is above 0 `truncated` is also
|
|
2703
|
+
true. Sum it across a loop to know
|
|
2782
2704
|
how many entries went unclassified (or use `countOnly`, which refuses
|
|
2783
2705
|
instead).
|
|
2784
2706
|
- `countOnly?: true` — returns only the whole window's totals:
|
|
@@ -2794,7 +2716,7 @@ metadata is (the `autotask_describe` channel, `capabilities.json`,
|
|
|
2794
2716
|
and dropped another); a total equal to either count is accepted, so a
|
|
2795
2717
|
window that grows or shrinks by the time the walk ends still succeeds, and
|
|
2796
2718
|
one that changes mid-walk can fail or pass depending on timing: retry.
|
|
2797
|
-
`limit` and `
|
|
2719
|
+
`limit` and `state` do not apply; `cursor` is rejected with `countOnly`
|
|
2798
2720
|
(CONFIG_ERROR).
|
|
2799
2721
|
- `maxVendorCalls?: number` — the `countOnly` budget (integer 1-200,
|
|
2800
2722
|
default 30). The estimate is `1 + max(1, ceil(n / 500)) + ceil(n / 250) + 1`
|
|
@@ -2880,7 +2802,7 @@ resources, `include: ['fields']`; the closed opts bag is documented in
|
|
|
2880
2802
|
the registry record). One bounded entityInformation GET per resource ×
|
|
2881
2803
|
kind; the wire cost `resources × include` is REFUSED pre-request above
|
|
2882
2804
|
the pinned cap (`MAX_FIELD_CATALOG_WIRE = 60`) — refused, never
|
|
2883
|
-
truncated (the
|
|
2805
|
+
truncated (the bounded sweep contract). Per-resource vendor
|
|
2884
2806
|
failures are recorded on the row (`error: { code, message }`) and never
|
|
2885
2807
|
abort the sweep. Capability is PLAN-DERIVED, never hardcoded: the
|
|
2886
2808
|
`getFieldInfo` / `getUdfInfo` row sets and each resource's vendor path
|
|
@@ -3393,58 +3315,35 @@ call.
|
|
|
3393
3315
|
Pins: `test/operations/expandResourceNames.test.ts` (the plan row's `tests[]`)
|
|
3394
3316
|
and `test/resources/cache-write-invalidation.test.ts` (mock wire only).
|
|
3395
3317
|
|
|
3396
|
-
### 11.22
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
invoices
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
dropped the first day and added an extra day; zones ahead of UTC returned the
|
|
3426
|
-
right rows by accident). A zone-converted instant window picks the wrong day
|
|
3427
|
-
behind UTC. DST is
|
|
3428
|
-
handled by inverting local midnights to UTC instants (a local day that
|
|
3429
|
-
gains or loses an hour still pins both boundaries to its TRUE midnights —
|
|
3430
|
-
never clock-hour arithmetic); `last24h` is rolling (`now − 24h → now`)
|
|
3431
|
-
and TZ-invariant by construction. A zone `Intl.DateTimeFormat` rejects is
|
|
3432
|
-
refused pre-request as a typed `CONFIG_ERROR` (zero wire). There is no
|
|
3433
|
-
config-level timezone in v1 (the zone is per-call; a future
|
|
3434
|
-
`config.timeZone` default would be an additive follow-up).
|
|
3435
|
-
- **The existing family gains the same opt** — the 12 `time_entries`
|
|
3436
|
-
window ops (incl. `customRange`) plus `postedWithin` and its
|
|
3437
|
-
`dateWorkedWindow` alias accept `opts.tz`
|
|
3438
|
-
with the contract above (date-only for time entries), so the whole window family accepts
|
|
3439
|
-
the same `tz` option (default UTC unchanged).
|
|
3440
|
-
- **Shape and wire** — each op is exactly ONE bounded query (`MaxRecords`
|
|
3441
|
-
1-500, default 500 — the hard cap, issue #128: a call without `limit` takes
|
|
3442
|
-
the first 500 rows in id order; a window with more than 500 matching rows
|
|
3443
|
-
needs the bounded query op with the same date-window filter and a suitable
|
|
3444
|
-
`limit` or explicit `walk.maxRows` cap (section 4); the `itemDate` /
|
|
3445
|
-
`invoiceDateTime` `gte`/`lte` filter rides the
|
|
3446
|
-
wire), and the result is the plain `items[]` array — no counts
|
|
3447
|
-
(the time-entry family shape).
|
|
3318
|
+
### 11.22 The cut date-window families — `dateWorkedWindow` and bounded queries (issue #66, consolidated in #290)
|
|
3319
|
+
|
|
3320
|
+
Issue #66 made the window zone EXPLICIT (a `tz` option, default UTC) and
|
|
3321
|
+
mirrored the time-entry window family onto `billing_items` (`itemDate`) and
|
|
3322
|
+
`invoices` (`invoiceDateTime`) — twelve relative ops each. The consolidation
|
|
3323
|
+
(issue #290) cut all of them: ONLY the `time_entries` family is consolidated
|
|
3324
|
+
into the single op `time_entries.dateWorkedWindow(from, to, opts?)` (§11.13)
|
|
3325
|
+
— an inclusive, UTC-anchored `dateWorked` window with the posted/unposted
|
|
3326
|
+
split. The `billing_items` and `invoices` date-window families are CUT with
|
|
3327
|
+
no replacement op: reading those resources by date window goes through their
|
|
3328
|
+
bounded `query` ops on `itemDate` / `invoiceDateTime` (bullet below). The
|
|
3329
|
+
`tz` option is gone (`dateWorked` is a date-only value at 00:00 UTC; the
|
|
3330
|
+
`from`/`to` days ARE the wire bounds, never shifted, and impossible days are
|
|
3331
|
+
refused pre-request).
|
|
3332
|
+
|
|
3333
|
+
- **Reading billing items / invoices by date window** — use the bounded `query`
|
|
3334
|
+
ops: `billing_items.query` with an `itemDate` `gte`/`lte` filter on whole UTC
|
|
3335
|
+
days (`POST /V1.0/BillingItems/query`) and `invoices.query` with
|
|
3336
|
+
`invoiceDateTime` (`POST /V1.0/Invoices/query` — the invoices model has no
|
|
3337
|
+
plain `invoiceDate`; `fromDate`/`toDate` are range fields, not the invoice
|
|
3338
|
+
date). Each call is ONE bounded query (`MaxRecords` 1-500, default 500 — the
|
|
3339
|
+
hard cap, issue #128), the plain `items[]` result, no counts.
|
|
3340
|
+
- **The zone contract (history)** — before #290 the relative windows
|
|
3341
|
+
(`today`, `lastFullMonth`, ...) computed the calendar dates in `opts.tz` and
|
|
3342
|
+
sent whole UTC days (DST handled by inverting local midnights to UTC instants;
|
|
3343
|
+
`last24h` was rolling, `now − 24h → now`, TZ-invariant by construction); a
|
|
3344
|
+
zone the vendor's `Intl.DateTimeFormat` rejects was refused pre-request as a
|
|
3345
|
+
typed `CONFIG_ERROR` (zero wire). No relative-window op remains, so the
|
|
3346
|
+
contract only matters for historical receipts.
|
|
3448
3347
|
|
|
3449
3348
|
### 11.23 Free-text caps (issue #61)
|
|
3450
3349
|
|
|
@@ -3660,8 +3559,8 @@ returns the contract's reconciled P&L over a date window:
|
|
|
3660
3559
|
(ticket `createDate`) as UTC instants, and bucketing slices the ISO
|
|
3661
3560
|
string — in a UTC+10/11 tenant a ticket created late local time buckets
|
|
3662
3561
|
one UTC day (possibly one month) earlier than its local date. The
|
|
3663
|
-
|
|
3664
|
-
|
|
3562
|
+
posted-state ops are gone (cut in #290 — the profitability fold
|
|
3563
|
+
already holds the billing items, so the join is
|
|
3665
3564
|
in-memory with no extra wire); posted/unposted derives from the
|
|
3666
3565
|
`billingItems.timeEntryID` join (unposted = no billing item exists yet).
|
|
3667
3566
|
The structural exclusions (`/Services/`, `/ExpenseReports/`,
|
|
@@ -3742,7 +3641,7 @@ returns the contract's reconciled P&L over a date window:
|
|
|
3742
3641
|
SERVICE lines)` (labour and charges never mask a missed recurring
|
|
3743
3642
|
invoice) · `unposted_time { entries, hours, hoursAbsent?, value, absent? }`
|
|
3744
3643
|
(billable time with no billing item yet — derived from the billing join,
|
|
3745
|
-
never
|
|
3644
|
+
never a posted-state op (cut in #290); `hours` = Σ `max(0, hoursToBill)`, or `null`
|
|
3746
3645
|
+ `hoursAbsent: 'HOURS_TO_BILL_MISSING'` when any of the month's unposted
|
|
3747
3646
|
entries carries no numeric `hoursToBill` — unknown, never a summed 0; a
|
|
3748
3647
|
deliberate `hoursToBill: 0` stays 0; `value` = Σ `hoursToBill ×
|
|
@@ -3925,8 +3824,8 @@ returns the contract's reconciled P&L over a date window:
|
|
|
3925
3824
|
### 11.27 Label siblings on query, aggregate and fetch (opt-in, issue #287)
|
|
3926
3825
|
|
|
3927
3826
|
Rows served by the invoke `<resource>.query` op (the bounded query), the group
|
|
3928
|
-
keys of `operations.aggregate`, and the found records of `operations.
|
|
3929
|
-
|
|
3827
|
+
keys of `operations.aggregate`, and the found records of `operations.fetchByIds`
|
|
3828
|
+
carry label siblings **when opted in** (issue #287:
|
|
3930
3829
|
OFF by default — the label pass costs a field-catalog read plus up to 20 cold
|
|
3931
3830
|
name reads per call, so a plain read returns raw ids), through the same
|
|
3932
3831
|
`resolveCompositeNames` path the full-context composites use:
|
|
@@ -3935,9 +3834,9 @@ name reads per call, so a plain read returns raw ids), through the same
|
|
|
3935
3834
|
siblings on for every read surface of that client (the MCP server sets it,
|
|
3936
3835
|
server-side).
|
|
3937
3836
|
- **The per-call key:** `names: true` in the query bag / the aggregate opts /
|
|
3938
|
-
the `
|
|
3939
|
-
|
|
3940
|
-
|
|
3837
|
+
the `fetchByIds` opts turns them on for that call; `names: false` turns them
|
|
3838
|
+
off (even when the client has `labels: true`); ABSENT = the client's option
|
|
3839
|
+
(the off default). A non-boolean `names` is refused
|
|
3941
3840
|
pre-request.
|
|
3942
3841
|
|
|
3943
3842
|
| field on the row | sibling | example |
|
|
@@ -4000,9 +3899,8 @@ name reads per call, so a plain read returns raw ids), through the same
|
|
|
4000
3899
|
per-call cap of 20 uncached name reads was hit — names already read are
|
|
4001
3900
|
cached, so repeating or narrowing the call labels more"`. Unresolvable
|
|
4002
3901
|
picklists add a note clause and a `reasons` entry per field (the
|
|
4003
|
-
field-catalog honesty note / the value-not-served note); on `
|
|
4004
|
-
|
|
4005
|
-
field name. Absent `names` = every sibling was resolved.
|
|
3902
|
+
field-catalog honesty note / the value-not-served note); on `fetchByIds`
|
|
3903
|
+
the notes merge across resources and `reasons` keys the plain field name. Absent `names` = every sibling was resolved.
|
|
4006
3904
|
- Response size (full model, wrapped names): a tickets row grows ~40% (live,
|
|
4007
3905
|
100 rows, 31 siblings per row, some of them null: 341,178 B -> 477,325 B;
|
|
4008
3906
|
more when every FK is populated), a time_entries row ~77% (worst case, every
|