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.
Files changed (47) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/MCP_TOOL_CATALOG.json +153 -1269
  3. package/MCP_TOOL_MANIFEST.md +28 -95
  4. package/MCP_TOOL_OVERRIDES.json +8 -62
  5. package/N8N_MCP_MANIFEST.json +118 -695
  6. package/N8N_MCP_MANIFEST.md +32 -52
  7. package/README.md +5 -10
  8. package/capabilities.json +23788 -31075
  9. package/dist/{base-CftidiyZ.d.cts → base-BVoYDlVA.d.cts} +2 -2
  10. package/dist/{base-CftidiyZ.d.ts → base-BVoYDlVA.d.ts} +2 -2
  11. package/dist/capabilities.cjs +2 -2
  12. package/dist/capabilities.js +1 -1
  13. package/dist/{chunk-N6IWPGHB.js → chunk-ASMCTZO2.js} +23787 -31074
  14. package/dist/{chunk-6WLGHKIU.cjs → chunk-DC3PY4EF.cjs} +1839 -1407
  15. package/dist/{chunk-BKTOMNT2.js → chunk-DXKXQHCX.js} +5 -5
  16. package/dist/{chunk-KWQSGWY5.cjs → chunk-JO6WI763.cjs} +143 -143
  17. package/dist/{chunk-LYI5FRPX.js → chunk-NSW7JPVS.js} +11544 -13365
  18. package/dist/{chunk-7MCKJTSA.js → chunk-O4PIPUS4.js} +1583 -1151
  19. package/dist/{chunk-PH3ESYAF.cjs → chunk-VXVYFJH3.cjs} +23787 -31074
  20. package/dist/{chunk-EMYDC5SG.cjs → chunk-YMBVKVK4.cjs} +6123 -7944
  21. package/dist/{index-YbEk0awP.d.cts → index-C5g1H_hF.d.ts} +1194 -1415
  22. package/dist/{index-D8FtzUGP.d.ts → index-Db0SNnQn.d.cts} +1194 -1415
  23. package/dist/{index-DgBA1JMH.d.cts → index-c549LvOb.d.cts} +188 -188
  24. package/dist/{index-DgBA1JMH.d.ts → index-c549LvOb.d.ts} +188 -188
  25. package/dist/index.cjs +22 -22
  26. package/dist/index.d.cts +15 -42
  27. package/dist/index.d.ts +15 -42
  28. package/dist/index.js +7 -7
  29. package/dist/operations/index.cjs +5 -2327
  30. package/dist/operations/index.d.cts +7 -287
  31. package/dist/operations/index.d.ts +7 -287
  32. package/dist/operations/index.js +29 -2351
  33. package/dist/pool.cjs +8 -8
  34. package/dist/pool.d.cts +1 -1
  35. package/dist/pool.d.ts +1 -1
  36. package/dist/pool.js +4 -4
  37. package/dist/resources/index.cjs +4 -4
  38. package/dist/resources/index.d.cts +2 -2
  39. package/dist/resources/index.d.ts +2 -2
  40. package/dist/resources/index.js +7 -7
  41. package/dist/types/index.d.cts +1 -1
  42. package/dist/types/index.d.ts +1 -1
  43. package/docs/API.md +201 -303
  44. package/docs/LIVE-TESTING.md +4 -6
  45. package/docs/N8N-ADAPTER.md +26 -26
  46. package/n8n-mcp.curation.json +3 -3
  47. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  # N8N MCP Manifest — node-autotask (consumer projection)
2
2
 
3
- <!-- GENERATED by scripts/project-n8n-mcp.mjs at 2026-11-01T01:23:14.190Z — do not hand-edit. -->
3
+ <!-- GENERATED by scripts/project-n8n-mcp.mjs at 2026-11-01T10:43:53.479Z — do not hand-edit. -->
4
4
 
5
5
  The n8n CONSUMER projection of the capability registry: the canonical source for what the
6
6
  n8n-side MCP tools must expose. The SDK is canonical — this is a projection of it, never a
@@ -24,19 +24,19 @@ user's n8n instances; PROD is read-only for any verification).
24
24
  | tool | mode | entity | readOnlyHint | destructiveHint | idempotentHint | openWorldHint | operations |
25
25
  |------|------|--------|--------------|-----------------|----------------|---------------|------------|
26
26
  | `autotask_ticket` | read | ticket | true | false | true | true | 17 |
27
- | `autotask_ticket_write` | write | ticket | false | false | false | true | 4 |
28
- | `autotask_contact` | read | contact | true | false | true | true | 10 |
29
- | `autotask_contact_write` | write | contact | false | false | false | true | 4 |
27
+ | `autotask_ticket_write` | write | ticket | false | false | false | true | 3 |
28
+ | `autotask_contact` | read | contact | true | false | true | true | 11 |
29
+ | `autotask_contact_write` | write | contact | false | false | false | true | 3 |
30
30
  | `autotask_company` | read | company | true | false | true | true | 14 |
31
- | `autotask_company_write` | write | company | false | false | false | true | 3 |
31
+ | `autotask_company_write` | write | company | false | false | false | true | 2 |
32
32
  | `autotask_companyLocation` | read | companyLocation | true | false | true | true | 8 |
33
33
  | `autotask_companyLocation_write` | write | companyLocation | false | false | false | true | 2 |
34
34
  | `autotask_ticketNote` | read | ticketNote | true | false | true | true | 7 |
35
35
  | `autotask_ticketNote_write` | write | ticketNote | false | false | false | true | 2 |
36
- | `autotask_resource` | read | resource | true | false | true | true | 9 |
37
- | `autotask_resource_write` | write | resource | false | false | false | true | 2 |
38
- | `autotask_timeEntry` | read | timeEntry | true | false | true | true | 23 |
39
- | `autotask_timeEntry_write` | write | timeEntry | false | false | false | true | 5 |
36
+ | `autotask_resource` | read | resource | true | false | true | true | 10 |
37
+ | `autotask_resource_write` | write | resource | false | false | true | true | 1 |
38
+ | `autotask_timeEntry` | read | timeEntry | true | false | true | true | 8 |
39
+ | `autotask_timeEntry_write` | write | timeEntry | false | false | false | true | 2 |
40
40
 
41
41
  ### `autotask_ticket` (read — ticket)
42
42
 
@@ -64,25 +64,25 @@ user's n8n instances; PROD is read-only for any verification).
64
64
 
65
65
  ### `autotask_ticket_write` (write — ticket)
66
66
 
67
- > Write ticket records (mode: write — the registry operations with effect=write for tickets: changeStatus, create, openWithNoteAndAssign, update). Writes are dry-run first: dry_run defaults to true — the request is validated against the registry inputSchema and a DryRunResult envelope is returned; the mutating request is never issued unless the caller passes confirm (the n8n-side handler must force dry-run unless confirm is present, mirroring the SDK governor). Destructive operations are refused without the confirm flag — confirm must equal the operation key exactly (the deliberate act is the call). The n8n consumer surface excludes destructive operations by curation default (excludeDestructive); they remain available on the canonical SDK server (autotask_delete / per-op tools). By curation default (excludeDestructive) no destructive operation is included; destructive capability remains on the canonical SDK server surface. Per-operation required arguments: changeStatus [id, targetStatus]; create [data]; openWithNoteAndAssign [params]; update [id, data]. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
67
+ > Write ticket records (mode: write — the registry operations with effect=write for tickets: changeStatus, create, update). Writes are dry-run first: dry_run defaults to true — the request is validated against the registry inputSchema and a DryRunResult envelope is returned; the mutating request is never issued unless the caller passes confirm (the n8n-side handler must force dry-run unless confirm is present, mirroring the SDK governor). Destructive operations are refused without the confirm flag — confirm must equal the operation key exactly (the deliberate act is the call). The n8n consumer surface excludes destructive operations by curation default (excludeDestructive); they remain available on the canonical SDK server (autotask_delete / per-op tools). By curation default (excludeDestructive) no destructive operation is included; destructive capability remains on the canonical SDK server surface. Per-operation required arguments: changeStatus [id, targetStatus]; create [data]; update [id, data]. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
68
68
 
69
- Tool input: {"operation":{"type":"string","enum":["changeStatus","create","openWithNoteAndAssign","update"]},"id":{"type":"number | string (the ticket id — a safe integer, or a canonical integer string for ids above 2^53)"},"targetStatus":{"type":"number (validated pre-request against the tenant's LIVE status picklist from the entity metadata when it is resolvable — a value the live picklist lacks → typed STATUS_UNKNOWN; when the metadata is unavailable the vendor's own validation decides)"},"fields":{"type":"object (optional) — extra ticket fields merged into the same PUT (the caller value wins over the node map defaults; the merge keeps every other current field)"},"data":{"type":"object","ref":"tickets body (see types)"},"params":{"type":"object — { companyID: number | string (the parent company id), title: string, priority?: number (default 3 Low), status?: number (default 1 New), queueID?: number (the node NEVER guesses a queue: omitted + vendor-500 → the typed QUEUE_REQUIRED error with the vendor message + discovery suggestedAction) }"},"note":{"type":"object (optional) — { description: string, title?: string, noteType?: number (default 1), publish?: number (default 1; integer picklist 1/2/4 — a JSON boolean 500s) }"},"assign":{"type":"object (optional) — { resourceID: number | string, roleID: number (required with resourceID) } (prechecked via resources.get BEFORE the create — RESOURCE_INVALID on missing/inactive; the resources table is blind to the API user, get is the authority)"},"dry_run":{"type":"boolean","default":true,"note":"true (the default) = the pinned DryRunResult envelope; the mutating request is never issued. The handler must force dry-run unless confirm is present."},"confirm":{"type":"string","note":"The operation key (e.g. \"tickets.create\") — its presence lifts the dry run. For destructive/approval-gated operations confirm must equal the operation key exactly; the call is refused pre-request without it."}} (required: operation)
69
+ Tool input: {"operation":{"type":"string","enum":["changeStatus","create","update"]},"id":{"type":"number | string (the ticket id — a safe integer, or a canonical integer string for ids above 2^53)"},"targetStatus":{"type":"number (validated pre-request against the tenant's LIVE status picklist from the entity metadata when it is resolvable — a value the live picklist lacks → typed STATUS_UNKNOWN; when the metadata is unavailable the vendor's own validation decides)"},"fields":{"type":"object (optional) — extra ticket fields merged into the same PUT (the caller value wins over the node map defaults; the merge keeps every other current field)"},"data":{"type":"object","ref":"tickets body (see types)"},"dry_run":{"type":"boolean","default":true,"note":"true (the default) = the pinned DryRunResult envelope; the mutating request is never issued. The handler must force dry-run unless confirm is present."},"confirm":{"type":"string","note":"The operation key (e.g. \"tickets.create\") — its presence lifts the dry run. For destructive/approval-gated operations confirm must equal the operation key exactly; the call is refused pre-request without it."}} (required: operation)
70
70
 
71
71
  | backingOperation | verb | effect | flags | requiredArgs | purpose |
72
72
  |------------------|------|--------|-------|--------------|---------|
73
73
  | `tickets.changeStatus` | changeStatus | write | — | id, targetStatus | Move a ticket to a target status in ONE op — the node validates targetStatus against the tenant's LIVE status picklist (entity metadata; vendor rejection is the fallback when the metadata is unavailable) + the live-discovered per-status required-field map and fills the proven-safe defaults itself (the vendor 500s when a target status has unsatisfied field requirements; the MCP LLM has no way to know the map). Dry-run first (dry_run). |
74
74
  | `tickets.create` | create | write | — | data | Create a ticket. Dry-run first (dry_run). |
75
- | `tickets.openWithNoteAndAssign` | openWithNoteAndAssign | write | — | params | Open a ticket with an optional first scoped note and an optional assignment in ONE op — the node absorbs the ww29-proven create contract and the note/assignment wire choreography (6 calls today → 1). Dry-run first (dry_run). |
76
75
  | `tickets.update` | update | write | idempotent | id, data | Update a ticket. Dry-run first (dry_run). Idempotent. |
77
76
 
78
77
  ### `autotask_contact` (read — contact)
79
78
 
80
- > Read contact records (mode: read — effect=read operations only; zero mutating requests, ungated by design). Registry operations for contacts: count: Count contact records matching a JSON filter. (under a company) | findByEmail: Find a contact by email address. | get: Get one Contact by id. | getContext: Get a contact with its primary context records in one call. (under a company) | getEntityInfo: Get the Contact capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). | getFieldInfo: Get the Contact field catalog (types, picklists, required, maxLength) — the full raw field dump (the largest read of the info trio; compact discovery: operations.fieldCatalog). | getUdfInfo: Get the Contact user-defined-field catalog. | query: Query Contact records with a JSON filter. | resolve: Resolve a Contact from a numeric id (the direct fetch, tried first) or an exact value of its primary text field(s) (firstName + lastName — exact match only). Returns the Contact record with the numeric id for get/update/delete. Other identifier forms (e.g. an email address) are NOT tried by resolve — use the resource's dedicated lookup op where one exists (see autotask_catalog). | search: Search contact records by text (contains on the searchable fields). (under a company). Bounds: Sensitive. Bounded: server-side pagination — ordinary limit default 500, max 2500; walk.maxRows independently sets the total cap, with pages at most min(500, limit); 1000-page guard; hasMore/truncated flag remaining rows. Sensitive. Bounded: limit default 25, max 500. Sensitive. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
79
+ > Read contact records (mode: read — effect=read operations only; zero mutating requests, ungated by design). Registry operations for contacts: count: Count contact records matching a JSON filter. (under a company) | findByEmail: Find a contact by email address. | fullProfile: Get a contact with its company and its open tickets in a single call: the contact (the parent-scoped root read, or the stage-1 row when the contact has no company FK), its company, and its open tickets — every part separately reported. | get: Get one Contact by id. | getContext: Get a contact with its primary context records in one call. (under a company) | getEntityInfo: Get the Contact capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). | getFieldInfo: Get the Contact field catalog (types, picklists, required, maxLength) — the full raw field dump (the largest read of the info trio; compact discovery: operations.fieldCatalog). | getUdfInfo: Get the Contact user-defined-field catalog. | query: Query Contact records with a JSON filter. | resolve: Resolve a Contact from a numeric id (the direct fetch, tried first) or an exact value of its primary text field(s) (firstName + lastName — exact match only). Returns the Contact record with the numeric id for get/update/delete. Other identifier forms (e.g. an email address) are NOT tried by resolve — use the resource's dedicated lookup op where one exists (see autotask_catalog). | search: Search contact records by text (contains on the searchable fields). (under a company). Bounds: Sensitive. Bounded: server-side pagination — ordinary limit default 500, max 2500; walk.maxRows independently sets the total cap, with pages at most min(500, limit); 1000-page guard; hasMore/truncated flag remaining rows. Sensitive. Bounded: limit default 25, max 500. Sensitive. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
81
80
 
82
81
  | backingOperation | verb | effect | flags | requiredArgs | purpose |
83
82
  |------------------|------|--------|-------|--------------|---------|
84
83
  | `contacts.count` | count | read | sensitive | none | Count contact records matching a JSON filter. (under a company) Sensitive. |
85
84
  | `contacts.findByEmail` | findByEmail | read | sensitive | value | Find a contact by email address. Sensitive. |
85
+ | `contacts.fullProfile` | fullProfile | read | — | id | Get a contact with its company and its open tickets in a single call: the contact (the parent-scoped root read, or the stage-1 row when the contact has no company FK), its company, and its open tickets — every part separately reported. |
86
86
  | `contacts.get` | get | read | sensitive | parentId, id | Get one Contact by id. Sensitive. |
87
87
  | `contacts.getContext` | getContext | read | sensitive | id | Get a contact with its primary context records in one call. (under a company) Sensitive. |
88
88
  | `contacts.getEntityInfo` | getEntityInfo | read | sensitive | none | Get the Contact capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). Sensitive. |
@@ -94,16 +94,15 @@ Tool input: {"operation":{"type":"string","enum":["changeStatus","create","openW
94
94
 
95
95
  ### `autotask_contact_write` (write — contact)
96
96
 
97
- > Write contact records (mode: write — the registry operations with effect=write for contacts: create, moveToCompany, update, upsertByEmail). Writes are dry-run first: dry_run defaults to true — the request is validated against the registry inputSchema and a DryRunResult envelope is returned; the mutating request is never issued unless the caller passes confirm (the n8n-side handler must force dry-run unless confirm is present, mirroring the SDK governor). Destructive operations are refused without the confirm flag — confirm must equal the operation key exactly (the deliberate act is the call). The n8n consumer surface excludes destructive operations by curation default (excludeDestructive); they remain available on the canonical SDK server (autotask_delete / per-op tools). By curation default (excludeDestructive) no destructive operation is included; destructive capability remains on the canonical SDK server surface. Approval-gated operations (contacts.moveToCompany) additionally require confirm equal to the operation key. Per-operation required arguments: create [parentId, data]; moveToCompany [sourceContactId, destinationCompanyId]; update [parentId, id, data]; upsertByEmail [companyID, email]. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
97
+ > Write contact records (mode: write — the registry operations with effect=write for contacts: create, moveToCompany, update). Writes are dry-run first: dry_run defaults to true — the request is validated against the registry inputSchema and a DryRunResult envelope is returned; the mutating request is never issued unless the caller passes confirm (the n8n-side handler must force dry-run unless confirm is present, mirroring the SDK governor). Destructive operations are refused without the confirm flag — confirm must equal the operation key exactly (the deliberate act is the call). The n8n consumer surface excludes destructive operations by curation default (excludeDestructive); they remain available on the canonical SDK server (autotask_delete / per-op tools). By curation default (excludeDestructive) no destructive operation is included; destructive capability remains on the canonical SDK server surface. Approval-gated operations (contacts.moveToCompany) additionally require confirm equal to the operation key. Per-operation required arguments: create [parentId, data]; moveToCompany [sourceContactId, destinationCompanyId]; update [parentId, id, data]. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
98
98
 
99
- Tool input: {"operation":{"type":"string","enum":["create","moveToCompany","update","upsertByEmail"]},"parentId":{"type":"number | string"},"data":{"type":"object","ref":"contacts body (see types)"},"sourceContactId":{"type":"number | string"},"destinationCompanyId":{"type":"number | string"},"id":{"type":"number | string"},"companyID":{"type":"number | string (the parent — the create MUST be scoped: the global /V1.0/Contacts collection has no POST)"},"email":{"type":"string (the natural dedup key — the vendor field emailAddress, exact match under the parent)"},"extra":{"type":"object (optional) — { firstName?: string (default: the email local-part), lastName?: string (default: empty string), isActive?: boolean | number (BOOLEAN ACCEPTED — mapped to the zone-correct number 1/0; a raw 0/1 number passes through) }"},"dry_run":{"type":"boolean","default":true,"note":"true (the default) = the pinned DryRunResult envelope; the mutating request is never issued. The handler must force dry-run unless confirm is present."},"confirm":{"type":"string","note":"The operation key (e.g. \"tickets.create\") — its presence lifts the dry run. For destructive/approval-gated operations confirm must equal the operation key exactly; the call is refused pre-request without it."}} (required: operation)
99
+ Tool input: {"operation":{"type":"string","enum":["create","moveToCompany","update"]},"parentId":{"type":"number | string"},"data":{"type":"object","ref":"contacts body (see types)"},"sourceContactId":{"type":"number | string"},"destinationCompanyId":{"type":"number | string"},"id":{"type":"number | string"},"dry_run":{"type":"boolean","default":true,"note":"true (the default) = the pinned DryRunResult envelope; the mutating request is never issued. The handler must force dry-run unless confirm is present."},"confirm":{"type":"string","note":"The operation key (e.g. \"tickets.create\") — its presence lifts the dry run. For destructive/approval-gated operations confirm must equal the operation key exactly; the call is refused pre-request without it."}} (required: operation)
100
100
 
101
101
  | backingOperation | verb | effect | flags | requiredArgs | purpose |
102
102
  |------------------|------|--------|-------|--------------|---------|
103
103
  | `contacts.create` | create | write | — | parentId, data | Create a contact. (under a company) Dry-run first (dry_run). |
104
104
  | `contacts.moveToCompany` | moveToCompany | write | requiresApproval+sensitive | sourceContactId, destinationCompanyId | Move a contact to a different company. Dry-run first (dry_run). Requires confirmation (confirm: contacts.moveToCompany). Sensitive. |
105
105
  | `contacts.update` | update | write | idempotent | parentId, id, data | Update a contact. (under a company) Dry-run first (dry_run). Idempotent. |
106
- | `contacts.upsertByEmail` | upsertByEmail | write | — | companyID, email | Make sure a contact with the given email exists under the given company — find-or-create under the RIGHT parent, with the typed EXISTS/CREATED result (+ the record). Dry-run first (dry_run). |
107
106
 
108
107
  ### `autotask_company` (read — company)
109
108
 
@@ -128,15 +127,14 @@ Tool input: {"operation":{"type":"string","enum":["create","moveToCompany","upda
128
127
 
129
128
  ### `autotask_company_write` (write — company)
130
129
 
131
- > Write company records (mode: write — the registry operations with effect=write for companies: create, update, upsertByName). Writes are dry-run first: dry_run defaults to true — the request is validated against the registry inputSchema and a DryRunResult envelope is returned; the mutating request is never issued unless the caller passes confirm (the n8n-side handler must force dry-run unless confirm is present, mirroring the SDK governor). Destructive operations are refused without the confirm flag — confirm must equal the operation key exactly (the deliberate act is the call). The n8n consumer surface excludes destructive operations by curation default (excludeDestructive); they remain available on the canonical SDK server (autotask_delete / per-op tools). By curation default (excludeDestructive) no destructive operation is included; destructive capability remains on the canonical SDK server surface. Per-operation required arguments: create [data]; update [id, data]; upsertByName [companyName, params]. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
130
+ > Write company records (mode: write — the registry operations with effect=write for companies: create, update). Writes are dry-run first: dry_run defaults to true — the request is validated against the registry inputSchema and a DryRunResult envelope is returned; the mutating request is never issued unless the caller passes confirm (the n8n-side handler must force dry-run unless confirm is present, mirroring the SDK governor). Destructive operations are refused without the confirm flag — confirm must equal the operation key exactly (the deliberate act is the call). The n8n consumer surface excludes destructive operations by curation default (excludeDestructive); they remain available on the canonical SDK server (autotask_delete / per-op tools). By curation default (excludeDestructive) no destructive operation is included; destructive capability remains on the canonical SDK server surface. Per-operation required arguments: create [data]; update [id, data]. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
132
131
 
133
- Tool input: {"operation":{"type":"string","enum":["create","update","upsertByName"]},"data":{"type":"object","ref":"companies body (see types)"},"id":{"type":"number | string"},"companyName":{"type":"string (the natural dedup key — exact match on the vendor field companyName; the dedup covers only companies this API user can enumerate — the unowned surface is blind)"},"params":{"type":"object — { phone: string, ownerResourceID?: number (REQUIRED on the create path — an active non-API-only resource; the node cannot enumerate candidates: the resources table is blind to the API user; absent + a dedup miss → the typed OWNER_RESOURCE_REQUIRED, zero wire), companyType?: number (default 1 — the ww29-proven value) }"},"dry_run":{"type":"boolean","default":true,"note":"true (the default) = the pinned DryRunResult envelope; the mutating request is never issued. The handler must force dry-run unless confirm is present."},"confirm":{"type":"string","note":"The operation key (e.g. \"tickets.create\") — its presence lifts the dry run. For destructive/approval-gated operations confirm must equal the operation key exactly; the call is refused pre-request without it."}} (required: operation)
132
+ Tool input: {"operation":{"type":"string","enum":["create","update"]},"data":{"type":"object","ref":"companies body (see types)"},"id":{"type":"number | string"},"dry_run":{"type":"boolean","default":true,"note":"true (the default) = the pinned DryRunResult envelope; the mutating request is never issued. The handler must force dry-run unless confirm is present."},"confirm":{"type":"string","note":"The operation key (e.g. \"tickets.create\") — its presence lifts the dry run. For destructive/approval-gated operations confirm must equal the operation key exactly; the call is refused pre-request without it."}} (required: operation)
134
133
 
135
134
  | backingOperation | verb | effect | flags | requiredArgs | purpose |
136
135
  |------------------|------|--------|-------|--------------|---------|
137
136
  | `companies.create` | create | write | — | data | Create a company. Dry-run first (dry_run). |
138
137
  | `companies.update` | update | write | idempotent | id, data | Update a company. Dry-run first (dry_run). Idempotent. |
139
- | `companies.upsertByName` | upsertByName | write | — | companyName, params | Make sure a company with the given name exists — find-or-create that carries the blind-surface boundary IN THE RESULT instead of pretending total visibility. Dry-run first (dry_run). |
140
138
 
141
139
  ### `autotask_companyLocation` (read — companyLocation)
142
140
 
@@ -191,7 +189,7 @@ Tool input: {"operation":{"type":"string","enum":["create","update"]},"parentId"
191
189
 
192
190
  ### `autotask_resource` (read — resource)
193
191
 
194
- > Read resource records (mode: read — effect=read operations only; zero mutating requests, ungated by design). Registry operations for resources: count: Count resource records matching a JSON filter. | get: Get one Resource by id. | getEntityInfo: Get the Resource capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). | getFieldInfo: Get the Resource field catalog (types, picklists, required, maxLength) — the full raw field dump (the largest read of the info trio; compact discovery: operations.fieldCatalog). | getUdfInfo: Get the Resource user-defined-field catalog. | query: Query Resource records with a JSON filter. | resolve: Resolve a resource from a numeric id (the direct fetch, tried first) or an exact value of its primary text field(s) (firstName + lastName — exact match only). Returns the Resource record with the numeric id for get/update/delete. Other identifier forms (e.g. an email address) are NOT tried by resolve — use the resource's dedicated lookup op where one exists (see autotask_catalog). | search: Search resource records by text (contains on the searchable fields). | whoAmI: Identify the resource the current credentials act as.. Bounds: Sensitive. Bounded: server-side pagination — ordinary limit default 500, max 2500; walk.maxRows independently sets the total cap, with pages at most min(500, limit); 1000-page guard; hasMore/truncated flag remaining rows. Sensitive. Bounded: limit default 25, max 500. Sensitive. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
192
+ > Read resource records (mode: read — effect=read operations only; zero mutating requests, ungated by design). Registry operations for resources: count: Count resource records matching a JSON filter. | get: Get one Resource by id. | getEntityInfo: Get the Resource capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). | getFieldInfo: Get the Resource field catalog (types, picklists, required, maxLength) — the full raw field dump (the largest read of the info trio; compact discovery: operations.fieldCatalog). | getUdfInfo: Get the Resource user-defined-field catalog. | query: Query Resource records with a JSON filter. | resolve: Resolve a resource from a numeric id (the direct fetch, tried first) or an exact value of its primary text field(s) (firstName + lastName — exact match only). Returns the Resource record with the numeric id for get/update/delete. Other identifier forms (e.g. an email address) are NOT tried by resolve — use the resource's dedicated lookup op where one exists (see autotask_catalog). | search: Search resource records by text (contains on the searchable fields). | whoAmI: Identify the resource the current credentials act as. | workload: Get a resource's workload in a single call: its open tickets (rows + the exact open totals), its task assignments and the tasks behind them, its time-off balances and requests.. Bounds: Sensitive. Bounded: server-side pagination — ordinary limit default 500, max 2500; walk.maxRows independently sets the total cap, with pages at most min(500, limit); 1000-page guard; hasMore/truncated flag remaining rows. Sensitive. Bounded: limit default 25, max 500. Sensitive. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
195
193
 
196
194
  | backingOperation | verb | effect | flags | requiredArgs | purpose |
197
195
  |------------------|------|--------|-------|--------------|---------|
@@ -204,60 +202,42 @@ Tool input: {"operation":{"type":"string","enum":["create","update"]},"parentId"
204
202
  | `resources.resolve` | resolve | read | sensitive | identifier | Resolve a resource from a numeric id (the direct fetch, tried first) or an exact value of its primary text field(s) (firstName + lastName — exact match only). Returns the Resource record with the numeric id for get/update/delete. Other identifier forms (e.g. an email address) are NOT tried by resolve — use the resource's dedicated lookup op where one exists (see autotask_catalog). Sensitive. |
205
203
  | `resources.search` | search | read | sensitive | query | Search resource records by text (contains on the searchable fields). Bounded: limit default 25, max 500. Sensitive. |
206
204
  | `resources.whoAmI` | whoAmI | read | sensitive | none | Identify the resource the current credentials act as. Sensitive. |
205
+ | `resources.workload` | workload | read | — | id | Get a resource's workload in a single call: its open tickets (rows + the exact open totals), its task assignments and the tasks behind them, its time-off balances and requests. |
207
206
 
208
207
  ### `autotask_resource_write` (write — resource)
209
208
 
210
- > Write resource records (mode: write — the registry operations with effect=write for resources: transferOwnership, update). Writes are dry-run first: dry_run defaults to true — the request is validated against the registry inputSchema and a DryRunResult envelope is returned; the mutating request is never issued unless the caller passes confirm (the n8n-side handler must force dry-run unless confirm is present, mirroring the SDK governor). Destructive operations are refused without the confirm flag — confirm must equal the operation key exactly (the deliberate act is the call). The n8n consumer surface excludes destructive operations by curation default (excludeDestructive); they remain available on the canonical SDK server (autotask_delete / per-op tools). By curation default (excludeDestructive) no destructive operation is included; destructive capability remains on the canonical SDK server surface. Approval-gated operations (resources.transferOwnership) additionally require confirm equal to the operation key. Per-operation required arguments: transferOwnership [sourceResourceIds, destinationResourceId]; update [id, data]. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
209
+ > Write resource records (mode: write — the registry operations with effect=write for resources: update). Writes are dry-run first: dry_run defaults to true — the request is validated against the registry inputSchema and a DryRunResult envelope is returned; the mutating request is never issued unless the caller passes confirm (the n8n-side handler must force dry-run unless confirm is present, mirroring the SDK governor). Destructive operations are refused without the confirm flag — confirm must equal the operation key exactly (the deliberate act is the call). The n8n consumer surface excludes destructive operations by curation default (excludeDestructive); they remain available on the canonical SDK server (autotask_delete / per-op tools). By curation default (excludeDestructive) no destructive operation is included; destructive capability remains on the canonical SDK server surface. Per-operation required arguments: update [id, data]. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
211
210
 
212
- Tool input: {"operation":{"type":"string","enum":["transferOwnership","update"]},"sourceResourceIds":{"type":"(number | string)[] (max 500)"},"destinationResourceId":{"type":"number | string"},"id":{"type":"number | string"},"data":{"type":"object","ref":"resources body (see types)"},"dry_run":{"type":"boolean","default":true,"note":"true (the default) = the pinned DryRunResult envelope; the mutating request is never issued. The handler must force dry-run unless confirm is present."},"confirm":{"type":"string","note":"The operation key (e.g. \"tickets.create\") — its presence lifts the dry run. For destructive/approval-gated operations confirm must equal the operation key exactly; the call is refused pre-request without it."}} (required: operation)
211
+ Tool input: {"operation":{"type":"string","enum":["update"]},"id":{"type":"number | string"},"data":{"type":"object","ref":"resources body (see types)"},"dry_run":{"type":"boolean","default":true,"note":"true (the default) = the pinned DryRunResult envelope; the mutating request is never issued. The handler must force dry-run unless confirm is present."},"confirm":{"type":"string","note":"The operation key (e.g. \"tickets.create\") — its presence lifts the dry run. For destructive/approval-gated operations confirm must equal the operation key exactly; the call is refused pre-request without it."}} (required: operation)
213
212
 
214
213
  | backingOperation | verb | effect | flags | requiredArgs | purpose |
215
214
  |------------------|------|--------|-------|--------------|---------|
216
- | `resources.transferOwnership` | transferOwnership | write | requiresApproval+sensitive | sourceResourceIds, destinationResourceId | Transfer ownership of named resources to another resource. Dry-run first (dry_run). Requires confirmation (confirm: resources.transferOwnership). Sensitive. |
217
215
  | `resources.update` | update | write | idempotent+sensitive | id, data | Update a resource. Dry-run first (dry_run). Idempotent. Sensitive. |
218
216
 
219
217
  ### `autotask_timeEntry` (read — timeEntry)
220
218
 
221
- > Read timeEntry records (mode: read — effect=read operations only; zero mutating requests, ungated by design). Registry operations for time_entries: count: Count time entry records matching a JSON filter. | customRange: Get time entries for an explicit dateWorked range. | dateWorkedWindow: The unambiguous-named alias for time_entries.postedWithin (issue #66): the dateWorked window read with the posted/unposted split derived from the billing_items.timeEntryID join — same wire, same result (incl. truncated). The window is ON dateWorked (entries worked within [from, to]); "postedWithin" keeps its historical name (its "posted" is the posted/unposted split, never a posted-date window or a postedDate read). | get: Get one TimeEntry by id. | getEntityInfo: Get the TimeEntry capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). | getFieldInfo: Get the TimeEntry field catalog (types, picklists, required, maxLength) — the full raw field dump (the largest read of the info trio; compact discovery: operations.fieldCatalog). | getPosted: Get posted time entries (an entry that a billing item references), optionally for one ticket and/or a dateWorked window — posted state derives from the billing_items.timeEntryID join, never a postedDate read or approval. | getUdfInfo: Get the TimeEntry user-defined-field catalog. | getUnposted: Get unposted time entries (entries no billing item references), optionally for one ticket and/or a dateWorked window — posted state derives from the billing_items.timeEntryID join, never a postedDate read or approval. | last14: Get time entries for the last 14 days (rolling, dateWorked). | last24h: Get time entries for the last 24 hours (rolling, dateWorked). | last30: Get time entries for the last 30 days (rolling, dateWorked). | last7: Get time entries for the last 7 days (rolling, dateWorked). | last90: Get time entries for the last 90 days (rolling, dateWorked). | lastFullFortnight: Get time entries for the last complete fortnight. | lastFullMonth: Get time entries for the last complete calendar month. | lastFullQuarter: Get time entries for the last complete calendar quarter. | lastFullWeek: Get time entries for the last complete calendar week. | postedWithin: Get time entries WORKED within an inclusive dateWorked window with the posted/unposted split. The window is ON the dateWorked field — the name's "posted" is the posted/unposted SPLIT derived from the billing_items.timeEntryID join (an entry a billing item references is posted, approval is not consulted), never a posted-date window or a postedDate read. The counts cover the returned rows only; truncated flags a capped read. time_entries.dateWorkedWindow is the unambiguous name. | query: Query TimeEntry records with a JSON filter. | resolve: Resolve a time entry from a numeric id (the direct fetch) only — this resource has no searchable text field, so a non-numeric identifier throws a config error. | today: Get time entries for today (dateWorked = now, server date). | yesterday: Get time entries for yesterday (dateWorked = yesterday, server date).. Bounds: Bounded: server-side pagination — ordinary limit default 500, max 2500; walk.maxRows independently sets the total cap, with pages at most min(500, limit); 1000-page guard; hasMore/truncated flag remaining rows. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
219
+ > Read timeEntry records (mode: read — effect=read operations only; zero mutating requests, ungated by design). Registry operations for time_entries: count: Count time entry records matching a JSON filter. | dateWorkedWindow: The ONE posted-state read (issue #290): a bounded read over the inclusive dateWorked window [from, to] (UTC 'YYYY-MM-DD' days) with the posted/unposted split derived from the billing_items.timeEntryID join — never a postedDate read (W-D3: this zone's TimeEntry entity has no postedDate; postedDate exist|notExist 500s, live ww29 2026-10-27). Approval is NOT consulted (ww29 never populates billingApprovalDateTime yet bills those entries). The no-window form is gone: read with no dateWorked filter goes through time_entries.query. | get: Get one TimeEntry by id. | getEntityInfo: Get the TimeEntry capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). | getFieldInfo: Get the TimeEntry field catalog (types, picklists, required, maxLength) — the full raw field dump (the largest read of the info trio; compact discovery: operations.fieldCatalog). | getUdfInfo: Get the TimeEntry user-defined-field catalog. | query: Query TimeEntry records with a JSON filter. | resolve: Resolve a time entry from a numeric id (the direct fetch) only — this resource has no searchable text field, so a non-numeric identifier throws a config error.. Bounds: Bounded: server-side pagination — ordinary limit default 500, max 2500; walk.maxRows independently sets the total cap, with pages at most min(500, limit); 1000-page guard; hasMore/truncated flag remaining rows. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
222
220
 
223
221
  | backingOperation | verb | effect | flags | requiredArgs | purpose |
224
222
  |------------------|------|--------|-------|--------------|---------|
225
223
  | `time_entries.count` | count | read | — | none | Count time entry records matching a JSON filter. |
226
- | `time_entries.customRange` | customRange | read | — | from, to | Get time entries for an explicit dateWorked range. |
227
- | `time_entries.dateWorkedWindow` | dateWorkedWindow | read | — | from, to | The unambiguous-named alias for time_entries.postedWithin (issue #66): the dateWorked window read with the posted/unposted split derived from the billing_items.timeEntryID join — same wire, same result (incl. truncated). The window is ON dateWorked (entries worked within [from, to]); "postedWithin" keeps its historical name (its "posted" is the posted/unposted split, never a posted-date window or a postedDate read). |
224
+ | `time_entries.dateWorkedWindow` | dateWorkedWindow | read | — | from, to | The ONE posted-state read (issue #290): a bounded read over the inclusive dateWorked window [from, to] (UTC 'YYYY-MM-DD' days) with the posted/unposted split derived from the billing_items.timeEntryID join — never a postedDate read (W-D3: this zone's TimeEntry entity has no postedDate; postedDate exist|notExist 500s, live ww29 2026-10-27). Approval is NOT consulted (ww29 never populates billingApprovalDateTime yet bills those entries). The no-window form is gone: read with no dateWorked filter goes through time_entries.query. |
228
225
  | `time_entries.get` | get | read | — | id | Get one TimeEntry by id. |
229
226
  | `time_entries.getEntityInfo` | getEntityInfo | read | — | none | Get the TimeEntry capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). |
230
227
  | `time_entries.getFieldInfo` | getFieldInfo | read | — | none | Get the TimeEntry field catalog (types, picklists, required, maxLength) — the full raw field dump (the largest read of the info trio; compact discovery: operations.fieldCatalog). |
231
- | `time_entries.getPosted` | getPosted | read | — | none | Get posted time entries (an entry that a billing item references), optionally for one ticket and/or a dateWorked window — posted state derives from the billing_items.timeEntryID join, never a postedDate read or approval. |
232
228
  | `time_entries.getUdfInfo` | getUdfInfo | read | — | none | Get the TimeEntry user-defined-field catalog. |
233
- | `time_entries.getUnposted` | getUnposted | read | — | none | Get unposted time entries (entries no billing item references), optionally for one ticket and/or a dateWorked window — posted state derives from the billing_items.timeEntryID join, never a postedDate read or approval. |
234
- | `time_entries.last14` | last14 | read | — | none | Get time entries for the last 14 days (rolling, dateWorked). |
235
- | `time_entries.last24h` | last24h | read | — | none | Get time entries for the last 24 hours (rolling, dateWorked). |
236
- | `time_entries.last30` | last30 | read | — | none | Get time entries for the last 30 days (rolling, dateWorked). |
237
- | `time_entries.last7` | last7 | read | — | none | Get time entries for the last 7 days (rolling, dateWorked). |
238
- | `time_entries.last90` | last90 | read | — | none | Get time entries for the last 90 days (rolling, dateWorked). |
239
- | `time_entries.lastFullFortnight` | lastFullFortnight | read | — | none | Get time entries for the last complete fortnight. |
240
- | `time_entries.lastFullMonth` | lastFullMonth | read | — | none | Get time entries for the last complete calendar month. |
241
- | `time_entries.lastFullQuarter` | lastFullQuarter | read | — | none | Get time entries for the last complete calendar quarter. |
242
- | `time_entries.lastFullWeek` | lastFullWeek | read | — | none | Get time entries for the last complete calendar week. |
243
- | `time_entries.postedWithin` | postedWithin | read | — | from, to | Get time entries WORKED within an inclusive dateWorked window with the posted/unposted split. The window is ON the dateWorked field — the name's "posted" is the posted/unposted SPLIT derived from the billing_items.timeEntryID join (an entry a billing item references is posted, approval is not consulted), never a posted-date window or a postedDate read. The counts cover the returned rows only; truncated flags a capped read. time_entries.dateWorkedWindow is the unambiguous name. |
244
229
  | `time_entries.query` | query | read | — | none | Query TimeEntry records with a JSON filter. Bounded: server-side pagination — ordinary limit default 500, max 2500; walk.maxRows independently sets the total cap, with pages at most min(500, limit); 1000-page guard; hasMore/truncated flag remaining rows. |
245
230
  | `time_entries.resolve` | resolve | read | — | identifier | Resolve a time entry from a numeric id (the direct fetch) only — this resource has no searchable text field, so a non-numeric identifier throws a config error. |
246
- | `time_entries.today` | today | read | — | none | Get time entries for today (dateWorked = now, server date). |
247
- | `time_entries.yesterday` | yesterday | read | — | none | Get time entries for yesterday (dateWorked = yesterday, server date). |
248
231
 
249
232
  ### `autotask_timeEntry_write` (write — timeEntry)
250
233
 
251
- > Write timeEntry records (mode: write — the registry operations with effect=write for time_entries: approve, create, createForResource, createIfNotExists, update). Writes are dry-run first: dry_run defaults to true — the request is validated against the registry inputSchema and a DryRunResult envelope is returned; the mutating request is never issued unless the caller passes confirm (the n8n-side handler must force dry-run unless confirm is present, mirroring the SDK governor). Destructive operations are refused without the confirm flag — confirm must equal the operation key exactly (the deliberate act is the call). The n8n consumer surface excludes destructive operations by curation default (excludeDestructive); they remain available on the canonical SDK server (autotask_delete / per-op tools). By curation default (excludeDestructive) no destructive operation is included; destructive capability remains on the canonical SDK server surface. Per-operation required arguments: approve [id]; create [data]; createForResource [resourceID, params]; createIfNotExists [data]; update [id, data]. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
234
+ > Write timeEntry records (mode: write — the registry operations with effect=write for time_entries: create, update). Writes are dry-run first: dry_run defaults to true — the request is validated against the registry inputSchema and a DryRunResult envelope is returned; the mutating request is never issued unless the caller passes confirm (the n8n-side handler must force dry-run unless confirm is present, mirroring the SDK governor). Destructive operations are refused without the confirm flag — confirm must equal the operation key exactly (the deliberate act is the call). The n8n consumer surface excludes destructive operations by curation default (excludeDestructive); they remain available on the canonical SDK server (autotask_delete / per-op tools). By curation default (excludeDestructive) no destructive operation is included; destructive capability remains on the canonical SDK server surface. Per-operation required arguments: create [data]; update [id, data]. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
252
235
 
253
- Tool input: {"operation":{"type":"string","enum":["approve","create","createForResource","createIfNotExists","update"]},"id":{"type":"number | string"},"data":{"type":"object","ref":"time_entries body (see types)"},"resourceID":{"type":"number | string"},"params":{"type":"object — the smart-create params: { internallAllocationCodeID?: number (pin the internal linkage code), ticketID?: number, taskID?: number, roleID?: number (pin the task/ticket roleID — vendor-validated as an allocation code), dateWorked?: UTC YYYY-MM-DD, startDateTime?: UTC ISO, endDateTime?: UTC ISO, hoursWorked?: number, + any other vendor TimeEntry field } (exactly one linkage pin; none → the default internal linkage with the node-resolved code)"},"dry_run":{"type":"boolean","default":true,"note":"true (the default) = the pinned DryRunResult envelope; the mutating request is never issued. The handler must force dry-run unless confirm is present."},"confirm":{"type":"string","note":"The operation key (e.g. \"tickets.create\") — its presence lifts the dry run. For destructive/approval-gated operations confirm must equal the operation key exactly; the call is refused pre-request without it."}} (required: operation)
236
+ Tool input: {"operation":{"type":"string","enum":["create","update"]},"data":{"type":"object","ref":"time_entries body (see types)"},"id":{"type":"number | string"},"dry_run":{"type":"boolean","default":true,"note":"true (the default) = the pinned DryRunResult envelope; the mutating request is never issued. The handler must force dry-run unless confirm is present."},"confirm":{"type":"string","note":"The operation key (e.g. \"tickets.create\") — its presence lifts the dry run. For destructive/approval-gated operations confirm must equal the operation key exactly; the call is refused pre-request without it."}} (required: operation)
254
237
 
255
238
  | backingOperation | verb | effect | flags | requiredArgs | purpose |
256
239
  |------------------|------|--------|-------|--------------|---------|
257
- | `time_entries.approve` | approve | write | — | id | Approve a time entry (the REST-only approval path): create the BillingItemApprovalLevels record, apply the vendor-documented follow-up update of BillingApprovalDateTime + BillingApprovalResourceID, then read the entry back and report whether the vendor accepted it. Dry-run first (dry_run). |
258
240
  | `time_entries.create` | create | write | — | data | Create a time entry. Dry-run first (dry_run). |
259
- | `time_entries.createForResource` | createForResource | write | — | resourceID, params | Create a time entry for a resource with the allocation-code dependency resolved by the node: it verifies the resource is active, honours the caller's linkage pin (else the default internal linkage), resolves an EXISTING active allocation code applicable to the resource (never creates one — the API user cannot), and posts the vendor-valid body. Dry-run first (dry_run). |
260
- | `time_entries.createIfNotExists` | createIfNotExists | write | — | data | Create a TimeEntry. Dry-run first (dry_run). |
261
241
  | `time_entries.update` | update | write | idempotent | id, data | Update a time entry. Dry-run first (dry_run). Idempotent. |
262
242
 
263
243
  ## Destructive operations — excluded by default, available via the SDK server (P2 honesty)
@@ -275,19 +255,19 @@ only via a recorded change to `n8n-mcp.curation.json`.
275
255
 
276
256
  ## Stats
277
257
 
278
- - registryRecords: 1282
258
+ - registryRecords: 1224
279
259
  - curatedEntities: 7
280
260
  - includedEntities: 7
281
261
  - readTools: 7
282
262
  - writeTools: 7
283
263
  - totalTools: 14
284
- - readOperations: 88
285
- - writeOperations: 22
286
- - approvalGatedWrites: 2
264
+ - readOperations: 75
265
+ - writeOperations: 15
266
+ - approvalGatedWrites: 1
287
267
  - destructiveExcluded: 4
288
268
 
289
269
  ## Meta
290
270
 
291
- - generatedAt: 2026-11-01T01:23:14.190Z
292
- - capabilitiesHash: 7cf0032e0744a559430781aeafbd3984b708e71f04b850011e08fbf113eaadcc
293
- - curationHash: e19117ce73e38d9aded3be7e7f077a4e185d2e10294622b76c17d67f63cf9e3b
271
+ - generatedAt: 2026-11-01T10:43:53.479Z
272
+ - capabilitiesHash: 729ea0d47099fd45e1dde837510a92bd566a793368d0a4882e2613cbe05f7594
273
+ - curationHash: 623fbca25fa416f983dde22c8b736ce813a2db1bd8c188f7124abba710f52452
package/README.md CHANGED
@@ -343,9 +343,8 @@ The SDK ships the mechanical substrate, not a server (no MCP imports, no zod):
343
343
  governor place: `src/operations/invoke.ts`).
344
344
 
345
345
  <!-- BEGIN GENERATED:approval-policy -->
346
- **Harness approval policy (recommended).** Require approval on every tool whose MCP annotation is `readOnlyHint !== true` — today exactly **267 tools**: the 265 dedicated write/delete tools (209 `effect=write` + 56 `effect=destructive`, every one of them has a dedicated tool) + `autotask_write` + `autotask_delete`. The remaining **636 tools** (the 623 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`, `resources.transferOwnership`) 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.
346
+ **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.
347
347
  <!-- END GENERATED:approval-policy -->
348
-
349
348
  **Provider tool-search interop.** Hosts with provider-native tool search
350
349
  (OpenAI `type: "tool_search"` / `defer_loading`; Anthropic's tool search
351
350
  tool) defer-load tool arrays host-side. Two coexistences, both already
@@ -404,12 +403,8 @@ The SDK ships the mechanical substrate, not a server (no MCP imports, no zod):
404
403
  source.
405
404
 
406
405
  7. **Agent QoL surface (the stage-12 issue wave, live-verified on ww29)** —
407
- the zero-wire planning reads `operations.blindSurfaces` (the 124-surface
408
- blind/data inventory) and `operations.createGuidance` (per-child zone
409
- facts with verbatim vendor evidence), the bounded live
410
- `operations.probeReachability` sweep (confirm-gated, over-cap refuses),
411
- `operations.recovery` (find the records a marker-tagged write created;
412
- delete is dry-run-first under `confirm`), `time_entries.postedWithin`
406
+ the zero-wire planning read `operations.createGuidance` (per-child zone
407
+ facts with verbatim vendor evidence), `time_entries.postedWithin`
413
408
  (the inclusive UTC window read over `dateWorked` — posted/ticket/resource
414
409
  narrowing), the issue #13 create-path recovery (a drifted 2xx answers
415
410
  `idFrom: 'marker-recovery'` from ONE bounded marker read, or the typed
@@ -570,8 +565,8 @@ suites are the release gate, the live scripts are the ground-truth check.
570
565
  the 14-issue wave (agent profile + ClientPool/gateway mode, untrusted-content
571
566
  markers, the doctor preflight CLI, closed MCP input, wire-level surface probes
572
567
  + `npm run verify`, the Node-24 floor, the per-tenant 429 cooldown, delete
573
- boundaries + `operations.recovery`, the blind-surface inventory + bounded
574
- probe, the zone-fact create-guidance catalog, the posted/unposted rework
568
+ boundaries, the blind-surface inventory (the plan data the served artifacts
569
+ stamp from), the zone-fact create-guidance catalog, the posted/unposted rework
575
570
  (first the spec `postedDate` wire, replaced in 0.32.0 by the
576
571
  billing-item join because ww29 has no `postedDate`; W-D3 +
577
572
  `postedWithin`), the create-path marker recovery, the typed `emptyReason`,