@revfleet/hscli 0.8.12 → 0.9.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 (153) hide show
  1. package/CHANGELOG.md +166 -1
  2. package/README.md +220 -10
  3. package/dist/cli.js +27 -3
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/agent/contracts.d.ts +3 -0
  6. package/dist/commands/agent/contracts.js +139 -0
  7. package/dist/commands/agent/contracts.js.map +1 -0
  8. package/dist/commands/agent/index.d.ts +3 -0
  9. package/dist/commands/agent/index.js +188 -0
  10. package/dist/commands/agent/index.js.map +1 -0
  11. package/dist/commands/agent/recipes.d.ts +13 -0
  12. package/dist/commands/agent/recipes.js +108 -0
  13. package/dist/commands/agent/recipes.js.map +1 -0
  14. package/dist/commands/agent/runner.d.ts +16 -0
  15. package/dist/commands/agent/runner.js +165 -0
  16. package/dist/commands/agent/runner.js.map +1 -0
  17. package/dist/commands/auth/index.js +64 -7
  18. package/dist/commands/auth/index.js.map +1 -1
  19. package/dist/commands/automation/index.js +30 -0
  20. package/dist/commands/automation/index.js.map +1 -1
  21. package/dist/commands/backup/index.d.ts +12 -0
  22. package/dist/commands/backup/index.js +459 -0
  23. package/dist/commands/backup/index.js.map +1 -0
  24. package/dist/commands/cms/content-ops.js +1 -1
  25. package/dist/commands/cms/content-ops.js.map +1 -1
  26. package/dist/commands/communication-preferences/index.js +207 -7
  27. package/dist/commands/communication-preferences/index.js.map +1 -1
  28. package/dist/commands/content-permissions/index.d.ts +22 -0
  29. package/dist/commands/content-permissions/index.js +141 -0
  30. package/dist/commands/content-permissions/index.js.map +1 -0
  31. package/dist/commands/context/index.d.ts +3 -0
  32. package/dist/commands/context/index.js +182 -0
  33. package/dist/commands/context/index.js.map +1 -0
  34. package/dist/commands/crm/associations.js +38 -0
  35. package/dist/commands/crm/associations.js.map +1 -1
  36. package/dist/commands/crm/custom-objects.js +39 -2
  37. package/dist/commands/crm/custom-objects.js.map +1 -1
  38. package/dist/commands/crm/dated.js +3 -3
  39. package/dist/commands/crm/dated.js.map +1 -1
  40. package/dist/commands/crm/migration.js +210 -2
  41. package/dist/commands/crm/migration.js.map +1 -1
  42. package/dist/commands/crm/properties.js +2 -1
  43. package/dist/commands/crm/properties.js.map +1 -1
  44. package/dist/commands/crm/property-batch.d.ts +1 -0
  45. package/dist/commands/crm/property-batch.js +44 -5
  46. package/dist/commands/crm/property-batch.js.map +1 -1
  47. package/dist/commands/crm/shared.js +21 -2
  48. package/dist/commands/crm/shared.js.map +1 -1
  49. package/dist/commands/crm/sync.js +1 -1
  50. package/dist/commands/crm/sync.js.map +1 -1
  51. package/dist/commands/doctor/index.js +193 -0
  52. package/dist/commands/doctor/index.js.map +1 -1
  53. package/dist/commands/email-tracking/index.d.ts +16 -0
  54. package/dist/commands/email-tracking/index.js +314 -0
  55. package/dist/commands/email-tracking/index.js.map +1 -0
  56. package/dist/commands/extensions/index.js +1 -1
  57. package/dist/commands/extensions/index.js.map +1 -1
  58. package/dist/commands/forms/index.js +15 -2
  59. package/dist/commands/forms/index.js.map +1 -1
  60. package/dist/commands/forms/legacy-v2.d.ts +7 -2
  61. package/dist/commands/forms/legacy-v2.js +103 -7
  62. package/dist/commands/forms/legacy-v2.js.map +1 -1
  63. package/dist/commands/guide/index.d.ts +1 -1
  64. package/dist/commands/guide/index.js +37 -4
  65. package/dist/commands/guide/index.js.map +1 -1
  66. package/dist/commands/internal/catalog.d.ts +1 -0
  67. package/dist/commands/internal/catalog.js +2 -0
  68. package/dist/commands/internal/catalog.js.map +1 -0
  69. package/dist/commands/internal/index.d.ts +26 -0
  70. package/dist/commands/internal/index.js +823 -0
  71. package/dist/commands/internal/index.js.map +1 -0
  72. package/dist/commands/internal-session.d.ts +15 -0
  73. package/dist/commands/internal-session.js +152 -0
  74. package/dist/commands/internal-session.js.map +1 -0
  75. package/dist/commands/lists/index.js +1 -1
  76. package/dist/commands/lists/index.js.map +1 -1
  77. package/dist/commands/marketing/email-audit.d.ts +32 -0
  78. package/dist/commands/marketing/email-audit.js +121 -0
  79. package/dist/commands/marketing/email-audit.js.map +1 -0
  80. package/dist/commands/marketing/index.js +2 -0
  81. package/dist/commands/marketing/index.js.map +1 -1
  82. package/dist/commands/mcp.js +5 -3
  83. package/dist/commands/mcp.js.map +1 -1
  84. package/dist/commands/playbooks/index.d.ts +3 -0
  85. package/dist/commands/playbooks/index.js +228 -0
  86. package/dist/commands/playbooks/index.js.map +1 -0
  87. package/dist/commands/portals/index.d.ts +3 -0
  88. package/dist/commands/portals/index.js +132 -0
  89. package/dist/commands/portals/index.js.map +1 -0
  90. package/dist/commands/reports/index.d.ts +3 -0
  91. package/dist/commands/reports/index.js +479 -0
  92. package/dist/commands/reports/index.js.map +1 -0
  93. package/dist/commands/sales-content/index.d.ts +13 -0
  94. package/dist/commands/sales-content/index.js +59 -0
  95. package/dist/commands/sales-content/index.js.map +1 -0
  96. package/dist/commands/sales-templates/index.d.ts +3 -0
  97. package/dist/commands/sales-templates/index.js +88 -0
  98. package/dist/commands/sales-templates/index.js.map +1 -0
  99. package/dist/commands/seed/cms.js +28 -45
  100. package/dist/commands/seed/cms.js.map +1 -1
  101. package/dist/commands/sequences/index.d.ts +18 -0
  102. package/dist/commands/sequences/index.js +263 -0
  103. package/dist/commands/sequences/index.js.map +1 -0
  104. package/dist/commands/settings/index.js +450 -102
  105. package/dist/commands/settings/index.js.map +1 -1
  106. package/dist/commands/settings/site-settings.d.ts +3 -0
  107. package/dist/commands/settings/site-settings.js +106 -0
  108. package/dist/commands/settings/site-settings.js.map +1 -0
  109. package/dist/commands/ui/index.js +14 -4
  110. package/dist/commands/ui/index.js.map +1 -1
  111. package/dist/commands/workflows/index.js +2 -0
  112. package/dist/commands/workflows/index.js.map +1 -1
  113. package/dist/commands/workflows/preflight.d.ts +23 -0
  114. package/dist/commands/workflows/preflight.js +295 -0
  115. package/dist/commands/workflows/preflight.js.map +1 -0
  116. package/dist/core/action-contract.d.ts +43 -0
  117. package/dist/core/action-contract.js +207 -0
  118. package/dist/core/action-contract.js.map +1 -0
  119. package/dist/core/auth.d.ts +6 -0
  120. package/dist/core/auth.js +85 -0
  121. package/dist/core/auth.js.map +1 -1
  122. package/dist/core/http.d.ts +8 -0
  123. package/dist/core/http.js +37 -5
  124. package/dist/core/http.js.map +1 -1
  125. package/dist/core/id-maps.d.ts +27 -0
  126. package/dist/core/id-maps.js +204 -0
  127. package/dist/core/id-maps.js.map +1 -0
  128. package/dist/core/output.d.ts +2 -1
  129. package/dist/core/output.js +34 -0
  130. package/dist/core/output.js.map +1 -1
  131. package/dist/mcp/profiles.d.ts +14 -0
  132. package/dist/mcp/profiles.js +68 -0
  133. package/dist/mcp/profiles.js.map +1 -0
  134. package/dist/mcp/server.d.ts +5 -3
  135. package/dist/mcp/server.js +22 -5
  136. package/dist/mcp/server.js.map +1 -1
  137. package/docs/CAPABILITY_LIBRARY.md +29 -8
  138. package/docs/COMMAND_TREE.md +96 -17
  139. package/docs/COMPARISON.md +8 -5
  140. package/docs/COOKBOOK.md +124 -5
  141. package/docs/INTERNAL-ENDPOINTS-DISCOVERY.md +813 -0
  142. package/docs/INTERNAL-ENDPOINTS.md +371 -0
  143. package/docs/INTERNAL_ENDPOINTS.md +243 -0
  144. package/docs/MARKETING_SETUP.md +21 -1
  145. package/docs/MCP.md +28 -89
  146. package/docs/MCP_TOOL_CATALOG.md +192 -0
  147. package/docs/MIGRATION_ADAPTERS.md +115 -0
  148. package/docs/PORTAL_BACKUP.md +212 -0
  149. package/docs/PUBLISHING.md +1 -1
  150. package/docs/SALES_CONTENT_INTERNAL_PUNCH_LIST.md +504 -0
  151. package/docs/TUTORIALS/secure-agent-writes.md +49 -0
  152. package/docs/WHY_HOW_WHAT.md +10 -4
  153. package/package.json +4 -2
@@ -227,9 +227,29 @@ hscli forms create --source-format v2 --data '{"name":"Legacy form","submitText"
227
227
 
228
228
  # Real legacy writes preflight target CRM properties and skip missing fields; add --strict to fail instead
229
229
  hscli forms create --source-format v2 --strict --data '{"name":"Legacy form","formFieldGroups":[{"fields":[{"name":"email","label":"Email","fieldType":"email","required":true}]}]}' --dry-run
230
+
231
+ # Legacy consent checkboxes must use target subscription definition IDs
232
+ hscli forms create --source-format v2 --subscription-type-map ./id-maps/subscription-types.json --data '{"name":"Legacy form","formFieldGroups":[{"fields":[{"name":"email","label":"Email","fieldType":"email"}]}]}' --dry-run
230
233
  ```
231
234
 
232
- > **API:** `forms` scope. Full CRUD on forms including field configuration. Legacy forms/v2 payloads are auto-translated on create/update when detected; use `hscli forms translate-v2` to inspect the v3 payload before replay. During real writes, hscli preflights target CRM properties, skips missing fields by default, and auto-splits field groups to HubSpot's 3-field v3 limit.
235
+ > **API:** `forms` scope. Full CRUD on forms including field configuration. Legacy forms/v2 payloads are auto-translated on create/update when detected; use `hscli forms translate-v2` to inspect the v3 payload before replay. During real writes, hscli preflights target CRM properties, skips missing fields by default, auto-splits field groups to HubSpot's 3-field v3 limit, and requires `--subscription-type-map` when legacy consent metadata references source subscription IDs.
236
+
237
+ > **Subscription definition migration note:** Public APIs can read subscription
238
+ > definitions (`/communication-preferences/v3/definitions` and
239
+ > `/communication-preferences/v4/definitions`), but public create returned HTTP
240
+ > 405 during cross-portal migration testing. HubSpot's Email > Subscription
241
+ > Types UI writes through internal `/api/subscriptions/v1/definitions`. When
242
+ > using a browser-session migration helper, match by
243
+ > `name + purpose/process + communicationMethod/channel + businessUnitId` and
244
+ > remap business units before write.
245
+ >
246
+ > hscli exposes this as an explicit migration adapter:
247
+ > `hscli communication-preferences definitions create-internal --portal-id <id>
248
+ > --cookie-file <cookies> --data '{...}' --business-unit-map ./id-maps/business-units.json`.
249
+ > The adapter is Email-only and rejects non-Email `communicationMethod`,
250
+ > `channel`, or `method` values; Chat/Call/Post definitions need separately
251
+ > probed adapters.
252
+ > There is intentionally no generic internal API passthrough.
233
253
 
234
254
  ---
235
255
 
package/docs/MCP.md CHANGED
@@ -14,9 +14,16 @@ node dist/cli.js mcp
14
14
  Or if installed globally:
15
15
 
16
16
  ```bash
17
- hscli mcp
17
+ hscli mcp # minimal: essential CRM reads
18
+ hscli mcp --surface crm # curated CRM reads + writes
19
+ hscli mcp --surface migration # cross-product migration tools
20
+ hscli mcp --surface full # all tools, including raw API
18
21
  ```
19
22
 
23
+ The default `minimal` surface keeps agent context and write exposure small.
24
+ Set `HSCLI_MCP_SURFACE=minimal|crm|migration|full` when the MCP client cannot
25
+ pass command arguments.
26
+
20
27
  ## Profile isolation and safety defaults
21
28
 
22
29
  - **Profile isolation**: set `HSCLI_MCP_PROFILE=<profile>` to hard-lock the MCP server to one auth profile.
@@ -77,96 +84,28 @@ If your client accepts env vars, set:
77
84
  - `HSCLI_HOME` to the auth profile directory containing `auth.json`
78
85
  - `HSCLI_MCP_PROFILE` to force strict profile isolation
79
86
 
80
- ## Tool catalog
81
-
82
- ### Contacts
83
- - `crm_contacts_list`
84
- - `crm_contacts_get`
85
- - `crm_contacts_search`
86
- - `crm_contacts_create` *(write, dry-run by default)*
87
- - `crm_contacts_update` *(write, dry-run by default)*
88
- - `crm_contacts_delete` *(write, dry-run by default)*
89
- - `crm_contacts_merge` *(write, dry-run by default)*
90
- - `crm_contacts_batch_read`
91
- - `crm_contacts_batch_upsert` *(write, dry-run by default)*
92
- - `crm_contacts_batch_archive` *(write, dry-run by default)*
93
-
94
- ### Companies
95
- - `crm_companies_list`
96
- - `crm_companies_get`
97
- - `crm_companies_search`
98
- - `crm_companies_create` *(write, dry-run by default)*
99
- - `crm_companies_update` *(write, dry-run by default)*
100
- - `crm_companies_delete` *(write, dry-run by default)*
101
- - `crm_companies_merge` *(write, dry-run by default)*
102
- - `crm_companies_batch_read`
103
- - `crm_companies_batch_upsert` *(write, dry-run by default)*
104
- - `crm_companies_batch_archive` *(write, dry-run by default)*
105
-
106
- ### Deals
107
- - `crm_deals_list`
108
- - `crm_deals_get`
109
- - `crm_deals_search`
110
- - `crm_deals_create` *(write, dry-run by default)*
111
- - `crm_deals_update` *(write, dry-run by default)*
112
- - `crm_deals_delete` *(write, dry-run by default)*
113
- - `crm_deals_merge` *(write, dry-run by default)*
114
- - `crm_deals_batch_read`
115
- - `crm_deals_batch_upsert` *(write, dry-run by default)*
116
- - `crm_deals_batch_archive` *(write, dry-run by default)*
117
-
118
- ### Tickets
119
- - `crm_tickets_list`
120
- - `crm_tickets_get`
121
- - `crm_tickets_search`
122
- - `crm_tickets_create` *(write, dry-run by default)*
123
- - `crm_tickets_update` *(write, dry-run by default)*
124
- - `crm_tickets_delete` *(write, dry-run by default)*
125
- - `crm_tickets_merge` *(write, dry-run by default)*
126
- - `crm_tickets_batch_read`
127
- - `crm_tickets_batch_upsert` *(write, dry-run by default)*
128
- - `crm_tickets_batch_archive` *(write, dry-run by default)*
129
-
130
- ### Engagements
131
- - `crm_notes_*` (`list/get/search/create/update/delete`)
132
- - `crm_calls_*` (`list/get/search/create/update/delete`)
133
- - `crm_tasks_*` (`list/get/search/create/update/delete`)
134
- - `crm_emails_*` (`list/get/search/create/update/delete`)
135
- - `crm_meetings_*` (`list/get/search/create/update/delete`)
136
-
137
- ### Properties
138
- - `crm_properties_list`
139
- - `crm_properties_get`
140
- - `crm_properties_create` *(write, dry-run by default)*
141
- - `crm_properties_batch_create` *(write, dry-run by default; supports custom object type IDs, reserved-name skips, option cleanup, empty-enum demotion/skip)*
142
- - `crm_properties_update` *(write, dry-run by default)*
143
-
144
- ### Associations
145
- - `crm_associations_list`
146
- - `crm_associations_create` *(write, dry-run by default)*
147
- - `crm_associations_remove` *(write, dry-run by default)*
148
-
149
- ### Imports
150
- - `crm_imports_create` *(write, dry-run by default)*
151
- - `crm_imports_list`
152
- - `crm_imports_get`
153
- - `crm_imports_errors`
154
-
155
- ### Owners
156
- - `crm_owners_list`
157
-
158
- ### Pipelines
159
- - `crm_pipelines_list`
160
- - `crm_pipelines_get`
161
-
162
- ### Custom Objects
163
- - `crm_custom_schemas_list|get|create|update`
164
- - `crm_custom_records_list|get|search|create|update|delete`
165
-
166
- ### Raw API
167
- - `hub_api_request` *(write methods dry-run by default)*
87
+ ## Tool surfaces
88
+
89
+ | Surface | Tools | Intended use | Raw API |
90
+ |---|---:|---|---|
91
+ | `minimal` | 29 | Agent discovery and essential CRM reads | No |
92
+ | `crm` | 136 | CRM objects, schemas, properties, associations, imports | No |
93
+ | `migration` | 179 | Curated cross-product backup and migration work | No |
94
+ | `full` | 180 | Explicit operator access to every registered tool | Yes |
95
+
96
+ The exact catalog is generated from runtime registration metadata, including
97
+ domain, inferred risk, surfaces, and description:
98
+ [MCP_TOOL_CATALOG.md](MCP_TOOL_CATALOG.md).
99
+
100
+ Regenerate and verify it with:
101
+
102
+ ```bash
103
+ npm run docs:mcp
104
+ npm run docs:mcp:check
105
+ ```
168
106
 
169
107
  ## Notes
170
108
 
171
109
  - MCP transport is stdio only in this implementation.
172
110
  - Existing auth + HTTP core logic is reused (`getToken`, `HubSpotClient`, `maybeWrite`, redaction).
111
+ - `hub_api_request` is exposed only by `--surface full`.
@@ -0,0 +1,192 @@
1
+ # MCP Tool Catalog
2
+
3
+ Generated from the runtime registration schemas. Do not edit manually.
4
+
5
+ - Minimal: 29 tools
6
+ - CRM: 136 tools
7
+ - Migration: 179 tools
8
+ - Full: 180 tools
9
+
10
+ | Tool | Domain | Risk | Surfaces | Description |
11
+ |---|---|---|---|---|
12
+ | `cms_redirects_create` | cms | write | migration, full | Create a CMS URL redirect (dry-run by default unless force=true). Not available via HubSpot Remote MCP. |
13
+ | `cms_redirects_delete` | cms | delete | migration, full | Delete a CMS URL redirect (dry-run by default unless force=true). Not available via HubSpot Remote MCP. |
14
+ | `cms_redirects_list` | cms | read | migration, full | List CMS URL redirects. Not available via HubSpot Remote MCP. |
15
+ | `conversations_inboxes_list` | conversations | read | migration, full | List conversation inboxes. Not available via HubSpot Remote MCP. |
16
+ | `conversations_messages_send` | conversations | write | migration, full | Send a message into a conversation thread (dry-run by default unless force=true). Not available via HubSpot Remote MCP. |
17
+ | `conversations_threads_list` | conversations | read | migration, full | List conversation threads. Not available via HubSpot Remote MCP. |
18
+ | `crm_associations_create` | crm | write | crm, migration, full | Create default association (dry-run by default unless force=true). Supports standard types, engagements, and custom object type IDs. |
19
+ | `crm_associations_list` | crm | read | minimal, crm, migration, full | List associations between CRM objects (supports standard types, engagements, and custom object type IDs like 2-199622513) |
20
+ | `crm_associations_remove` | crm | delete | crm, migration, full | Remove default association (dry-run by default unless force=true). Supports standard types, engagements, and custom object type IDs. |
21
+ | `crm_calls_batch_archive` | crm | delete | crm, migration, full | Batch archive calls (dry-run by default unless force=true) |
22
+ | `crm_calls_batch_read` | crm | read | crm, migration, full | Batch read calls |
23
+ | `crm_calls_batch_upsert` | crm | write | crm, migration, full | Batch upsert calls (dry-run by default unless force=true) |
24
+ | `crm_calls_create` | crm | write | crm, migration, full | Create call (dry-run by default unless force=true) |
25
+ | `crm_calls_delete` | crm | delete | crm, migration, full | Delete/archive call by ID (dry-run by default unless force=true) |
26
+ | `crm_calls_get` | crm | read | crm, migration, full | Get one call by ID |
27
+ | `crm_calls_list` | crm | read | crm, migration, full | List calls |
28
+ | `crm_calls_merge` | crm | write | crm, migration, full | Merge calls records (endpoint support varies by object) |
29
+ | `crm_calls_search` | crm | read | crm, migration, full | Search calls |
30
+ | `crm_calls_update` | crm | write | crm, migration, full | Update call (dry-run by default unless force=true) |
31
+ | `crm_companies_batch_archive` | crm | delete | crm, migration, full | Batch archive companies (dry-run by default unless force=true) |
32
+ | `crm_companies_batch_read` | crm | read | minimal, crm, migration, full | Batch read companies |
33
+ | `crm_companies_batch_upsert` | crm | write | crm, migration, full | Batch upsert companies (dry-run by default unless force=true) |
34
+ | `crm_companies_create` | crm | write | crm, migration, full | Create companie (dry-run by default unless force=true) |
35
+ | `crm_companies_delete` | crm | delete | crm, migration, full | Delete/archive companie by ID (dry-run by default unless force=true) |
36
+ | `crm_companies_get` | crm | read | minimal, crm, migration, full | Get one companie by ID |
37
+ | `crm_companies_list` | crm | read | minimal, crm, migration, full | List companies |
38
+ | `crm_companies_merge` | crm | write | crm, migration, full | Merge companies records (endpoint support varies by object) |
39
+ | `crm_companies_search` | crm | read | minimal, crm, migration, full | Search companies |
40
+ | `crm_companies_update` | crm | write | crm, migration, full | Update companie (dry-run by default unless force=true) |
41
+ | `crm_contacts_batch_archive` | crm | delete | crm, migration, full | Batch archive contacts (dry-run by default unless force=true) |
42
+ | `crm_contacts_batch_read` | crm | read | minimal, crm, migration, full | Batch read contacts |
43
+ | `crm_contacts_batch_upsert` | crm | write | crm, migration, full | Batch upsert contacts (dry-run by default unless force=true) |
44
+ | `crm_contacts_create` | crm | write | crm, migration, full | Create contact (dry-run by default unless force=true) |
45
+ | `crm_contacts_delete` | crm | delete | crm, migration, full | Delete/archive contact by ID (dry-run by default unless force=true) |
46
+ | `crm_contacts_get` | crm | read | minimal, crm, migration, full | Get one contact by ID |
47
+ | `crm_contacts_list` | crm | read | minimal, crm, migration, full | List contacts |
48
+ | `crm_contacts_merge` | crm | write | crm, migration, full | Merge contacts records (endpoint support varies by object) |
49
+ | `crm_contacts_search` | crm | read | minimal, crm, migration, full | Search contacts |
50
+ | `crm_contacts_update` | crm | write | crm, migration, full | Update contact (dry-run by default unless force=true) |
51
+ | `crm_custom_records_create` | crm | write | crm, migration, full | Create custom object record (dry-run by default unless force=true) |
52
+ | `crm_custom_records_delete` | crm | delete | crm, migration, full | Delete custom object record (dry-run by default unless force=true) |
53
+ | `crm_custom_records_get` | crm | read | crm, migration, full | Get custom object record by objectType and id |
54
+ | `crm_custom_records_list` | crm | read | crm, migration, full | List custom object records by objectType |
55
+ | `crm_custom_records_search` | crm | read | crm, migration, full | Search custom object records |
56
+ | `crm_custom_records_update` | crm | write | crm, migration, full | Update custom object record (dry-run by default unless force=true) |
57
+ | `crm_custom_schemas_create` | crm | write | crm, migration, full | Create custom object schema (dry-run by default unless force=true) |
58
+ | `crm_custom_schemas_get` | crm | read | crm, migration, full | Get custom object schema by objectType |
59
+ | `crm_custom_schemas_list` | crm | read | crm, migration, full | List custom object schemas |
60
+ | `crm_custom_schemas_update` | crm | write | crm, migration, full | Update custom object schema (dry-run by default unless force=true) |
61
+ | `crm_deals_batch_archive` | crm | delete | crm, migration, full | Batch archive deals (dry-run by default unless force=true) |
62
+ | `crm_deals_batch_read` | crm | read | minimal, crm, migration, full | Batch read deals |
63
+ | `crm_deals_batch_upsert` | crm | write | crm, migration, full | Batch upsert deals (dry-run by default unless force=true) |
64
+ | `crm_deals_create` | crm | write | crm, migration, full | Create deal (dry-run by default unless force=true) |
65
+ | `crm_deals_delete` | crm | delete | crm, migration, full | Delete/archive deal by ID (dry-run by default unless force=true) |
66
+ | `crm_deals_get` | crm | read | minimal, crm, migration, full | Get one deal by ID |
67
+ | `crm_deals_list` | crm | read | minimal, crm, migration, full | List deals |
68
+ | `crm_deals_merge` | crm | write | crm, migration, full | Merge deals records (endpoint support varies by object) |
69
+ | `crm_deals_search` | crm | read | minimal, crm, migration, full | Search deals |
70
+ | `crm_deals_update` | crm | write | crm, migration, full | Update deal (dry-run by default unless force=true) |
71
+ | `crm_emails_batch_archive` | crm | delete | crm, migration, full | Batch archive emails (dry-run by default unless force=true) |
72
+ | `crm_emails_batch_read` | crm | read | crm, migration, full | Batch read emails |
73
+ | `crm_emails_batch_upsert` | crm | write | crm, migration, full | Batch upsert emails (dry-run by default unless force=true) |
74
+ | `crm_emails_create` | crm | write | crm, migration, full | Create email (dry-run by default unless force=true) |
75
+ | `crm_emails_delete` | crm | delete | crm, migration, full | Delete/archive email by ID (dry-run by default unless force=true) |
76
+ | `crm_emails_get` | crm | read | crm, migration, full | Get one email by ID |
77
+ | `crm_emails_list` | crm | read | crm, migration, full | List emails |
78
+ | `crm_emails_merge` | crm | write | crm, migration, full | Merge emails records (endpoint support varies by object) |
79
+ | `crm_emails_search` | crm | read | crm, migration, full | Search emails |
80
+ | `crm_emails_update` | crm | write | crm, migration, full | Update email (dry-run by default unless force=true) |
81
+ | `crm_exports_create` | crm | write | crm, migration, full | Create a CRM export (dry-run by default unless force=true) |
82
+ | `crm_exports_get` | crm | read | crm, migration, full | Get a CRM export by ID |
83
+ | `crm_exports_list` | crm | read | crm, migration, full | List CRM exports with pagination |
84
+ | `crm_exports_status` | crm | read | crm, migration, full | Get the status of a CRM export |
85
+ | `crm_imports_create` | crm | write | crm, migration, full | Create import (dry-run by default unless force=true) |
86
+ | `crm_imports_errors` | crm | read | crm, migration, full | Get import errors |
87
+ | `crm_imports_get` | crm | read | crm, migration, full | Get import by ID |
88
+ | `crm_imports_list` | crm | read | crm, migration, full | List import jobs |
89
+ | `crm_lists_create` | crm | write | crm, migration, full | Create a CRM list (dry-run by default unless force=true) |
90
+ | `crm_lists_delete` | crm | delete | crm, migration, full | Delete a CRM list (dry-run by default unless force=true) |
91
+ | `crm_lists_get` | crm | read | crm, migration, full | Get a CRM list by ID |
92
+ | `crm_lists_list` | crm | read | crm, migration, full | List CRM lists with pagination |
93
+ | `crm_lists_memberships` | crm | read | crm, migration, full | Get memberships of a CRM list |
94
+ | `crm_lists_update` | crm | write | crm, migration, full | Update a CRM list (dry-run by default unless force=true) |
95
+ | `crm_meetings_batch_archive` | crm | delete | crm, migration, full | Batch archive meetings (dry-run by default unless force=true) |
96
+ | `crm_meetings_batch_read` | crm | read | crm, migration, full | Batch read meetings |
97
+ | `crm_meetings_batch_upsert` | crm | write | crm, migration, full | Batch upsert meetings (dry-run by default unless force=true) |
98
+ | `crm_meetings_create` | crm | write | crm, migration, full | Create meeting (dry-run by default unless force=true) |
99
+ | `crm_meetings_delete` | crm | delete | crm, migration, full | Delete/archive meeting by ID (dry-run by default unless force=true) |
100
+ | `crm_meetings_get` | crm | read | crm, migration, full | Get one meeting by ID |
101
+ | `crm_meetings_list` | crm | read | crm, migration, full | List meetings |
102
+ | `crm_meetings_merge` | crm | write | crm, migration, full | Merge meetings records (endpoint support varies by object) |
103
+ | `crm_meetings_search` | crm | read | crm, migration, full | Search meetings |
104
+ | `crm_meetings_update` | crm | write | crm, migration, full | Update meeting (dry-run by default unless force=true) |
105
+ | `crm_notes_batch_archive` | crm | delete | crm, migration, full | Batch archive notes (dry-run by default unless force=true) |
106
+ | `crm_notes_batch_read` | crm | read | crm, migration, full | Batch read notes |
107
+ | `crm_notes_batch_upsert` | crm | write | crm, migration, full | Batch upsert notes (dry-run by default unless force=true) |
108
+ | `crm_notes_create` | crm | write | crm, migration, full | Create note (dry-run by default unless force=true) |
109
+ | `crm_notes_delete` | crm | delete | crm, migration, full | Delete/archive note by ID (dry-run by default unless force=true) |
110
+ | `crm_notes_get` | crm | read | crm, migration, full | Get one note by ID |
111
+ | `crm_notes_list` | crm | read | crm, migration, full | List notes |
112
+ | `crm_notes_merge` | crm | write | crm, migration, full | Merge notes records (endpoint support varies by object) |
113
+ | `crm_notes_search` | crm | read | crm, migration, full | Search notes |
114
+ | `crm_notes_update` | crm | write | crm, migration, full | Update note (dry-run by default unless force=true) |
115
+ | `crm_owners_list` | crm | read | minimal, crm, migration, full | List HubSpot owners |
116
+ | `crm_pipelines_get` | crm | read | minimal, crm, migration, full | Get one pipeline |
117
+ | `crm_pipelines_list` | crm | read | minimal, crm, migration, full | List pipelines for object type |
118
+ | `crm_pipelines_stages` | crm | read | minimal, crm, migration, full | List stages for a pipeline |
119
+ | `crm_properties_batch_create` | crm | write | crm, migration, full | Batch-create properties for a standard object or custom object type ID (dry-run by default unless force=true) |
120
+ | `crm_properties_create` | crm | write | crm, migration, full | Create property (dry-run by default unless force=true) |
121
+ | `crm_properties_get` | crm | read | minimal, crm, migration, full | Get one property definition |
122
+ | `crm_properties_list` | crm | read | minimal, crm, migration, full | List properties for an object type |
123
+ | `crm_properties_update` | crm | write | crm, migration, full | Update property (dry-run by default unless force=true) |
124
+ | `crm_property_groups_create` | crm | write | crm, migration, full | Create a property group (dry-run by default unless force=true) |
125
+ | `crm_property_groups_list` | crm | read | crm, migration, full | List property groups for an object type |
126
+ | `crm_property_groups_update` | crm | write | crm, migration, full | Update a property group (dry-run by default unless force=true) |
127
+ | `crm_tasks_batch_archive` | crm | delete | crm, migration, full | Batch archive tasks (dry-run by default unless force=true) |
128
+ | `crm_tasks_batch_read` | crm | read | crm, migration, full | Batch read tasks |
129
+ | `crm_tasks_batch_upsert` | crm | write | crm, migration, full | Batch upsert tasks (dry-run by default unless force=true) |
130
+ | `crm_tasks_create` | crm | write | crm, migration, full | Create task (dry-run by default unless force=true) |
131
+ | `crm_tasks_delete` | crm | delete | crm, migration, full | Delete/archive task by ID (dry-run by default unless force=true) |
132
+ | `crm_tasks_get` | crm | read | crm, migration, full | Get one task by ID |
133
+ | `crm_tasks_list` | crm | read | crm, migration, full | List tasks |
134
+ | `crm_tasks_merge` | crm | write | crm, migration, full | Merge tasks records (endpoint support varies by object) |
135
+ | `crm_tasks_search` | crm | read | crm, migration, full | Search tasks |
136
+ | `crm_tasks_update` | crm | write | crm, migration, full | Update task (dry-run by default unless force=true) |
137
+ | `crm_tickets_batch_archive` | crm | delete | crm, migration, full | Batch archive tickets (dry-run by default unless force=true) |
138
+ | `crm_tickets_batch_read` | crm | read | minimal, crm, migration, full | Batch read tickets |
139
+ | `crm_tickets_batch_upsert` | crm | write | crm, migration, full | Batch upsert tickets (dry-run by default unless force=true) |
140
+ | `crm_tickets_create` | crm | write | crm, migration, full | Create ticket (dry-run by default unless force=true) |
141
+ | `crm_tickets_delete` | crm | delete | crm, migration, full | Delete/archive ticket by ID (dry-run by default unless force=true) |
142
+ | `crm_tickets_get` | crm | read | minimal, crm, migration, full | Get one ticket by ID |
143
+ | `crm_tickets_list` | crm | read | minimal, crm, migration, full | List tickets |
144
+ | `crm_tickets_merge` | crm | write | crm, migration, full | Merge tickets records (endpoint support varies by object) |
145
+ | `crm_tickets_search` | crm | read | minimal, crm, migration, full | Search tickets |
146
+ | `crm_tickets_update` | crm | write | crm, migration, full | Update ticket (dry-run by default unless force=true) |
147
+ | `files_delete` | files | delete | migration, full | Permanently delete a file (dry-run by default unless force=true). Not available via HubSpot Remote MCP. |
148
+ | `files_get` | files | read | migration, full | Get metadata for a single file by ID. Not available via HubSpot Remote MCP. |
149
+ | `files_list` | files | read | migration, full | List uploaded files. Not available via HubSpot Remote MCP. |
150
+ | `files_signed_url` | files | read | migration, full | Generate a time-limited signed URL for a private file. Not available via HubSpot Remote MCP. |
151
+ | `forms_create` | forms | write | migration, full | Create a marketing form. Legacy forms/v2 payloads are translated to v3 automatically. Dry-run by default unless force=true. |
152
+ | `forms_get` | forms | read | migration, full | Get one form by ID. Not available via HubSpot Remote MCP. |
153
+ | `forms_list` | forms | read | migration, full | List marketing forms. Not available via HubSpot Remote MCP. |
154
+ | `forms_submissions` | forms | read | migration, full | List submissions for a form. Not available via HubSpot Remote MCP. |
155
+ | `forms_submit` | forms | write | migration, full | Programmatically submit a form (server-side submission endpoint). Dry-run by default unless force=true. |
156
+ | `forms_update` | forms | write | migration, full | Update a marketing form. Legacy forms/v2 payloads are translated to v3 automatically. Dry-run by default unless force=true. |
157
+ | `get_campaign_analytics` | compat | read | migration, full | Get campaign analytics (aggregated metrics) for one or more campaigns. HubSpot-compat. |
158
+ | `get_campaign_asset_metrics` | compat | read | migration, full | Get metrics for specific CRM objects associated with a campaign. HubSpot-compat. |
159
+ | `get_campaign_asset_types` | compat | read | migration, full | List available campaign asset types (emails, blog posts, ads, etc.). HubSpot-compat. |
160
+ | `get_campaign_contacts_by_type` | compat | read | migration, full | Paginated contact IDs for a campaign filtered by attribution type (e.g. 'INFLUENCED'). HubSpot-compat. |
161
+ | `get_crm_objects` | compat | read | minimal, crm, migration, full | Batch read CRM records by ID. HubSpot-compat: max 100 IDs per request. |
162
+ | `get_properties` | compat | read | minimal, crm, migration, full | Get full property definitions (types, enum options) for an object type. HubSpot-compat. |
163
+ | `get_user_details` | compat | read | minimal, crm, migration, full | Get the authenticated user's account info, portal details, and OAuth scopes. HubSpot-compat tool. |
164
+ | `hub_api_request` | hub | write | full | Raw HubSpot API request with safety controls |
165
+ | `hubdb_publish` | hubdb | write | migration, full | Publish a HubDB table's draft state to live (dry-run by default unless force=true). Not available via HubSpot Remote MCP. |
166
+ | `hubdb_row_create` | hubdb | write | migration, full | Create a row in a HubDB draft table (dry-run by default unless force=true). Call hubdb_publish to publish. Not available via HubSpot Remote MCP. |
167
+ | `hubdb_row_update` | hubdb | write | migration, full | Update a HubDB row in the draft table (dry-run by default unless force=true). Not available via HubSpot Remote MCP. |
168
+ | `hubdb_rows_list` | hubdb | read | migration, full | List rows in a HubDB table. Not available via HubSpot Remote MCP. |
169
+ | `hubdb_tables_list` | hubdb | read | migration, full | List HubDB tables. Not available via HubSpot Remote MCP. |
170
+ | `hubspot_module_compose` | modules | read | migration, full | Compose a widget body for a HubSpot drag-and-drop module using given field values. Validates the values against the module's fields.json schema before returning — rejects unknown fields and type mismatches. The returned widget is ready to drop into an email or CMS page's content.widgets[widgetId], with the schema + metadata HubSpot's renderer expects. Use this instead of hand-shaping widget bodies. |
171
+ | `hubspot_module_describe` | modules | read | migration, full | Return the full field schema for a HubSpot drag-and-drop module (the same schema a user sees when clicking a module in the editor). Fields include name, label, type (text, richtext, number, boolean, color, choice, group, image, link, ...), required flag, default, choices, and nested children for groups. Call this before hubspot_module_compose to know what fields to pass. |
172
+ | `hubspot_module_list` | modules | read | migration, full | Enumerate @hubspot/* built-in drag-and-drop modules available on the caller's portal. Each entry reports whether fields.json resolves (i.e. whether the module can be used in widgets). Useful for discovery — call hubspot_module_describe on any available entry to see its field schema. |
173
+ | `manage_crm_objects` | compat | write | crm, migration, full | Create, update, or DELETE CRM records. HubSpot-compat surface extended with 'delete' — policy-file + change-ticket still enforced server-side. Bulk max 100 records per call. |
174
+ | `marketing_emails_get` | marketing | read | migration, full | Get one marketing email by ID. Not available via HubSpot Remote MCP. |
175
+ | `marketing_emails_list` | marketing | read | migration, full | List marketing emails. Not available via HubSpot Remote MCP. |
176
+ | `marketing_emails_statistics` | marketing | read | migration, full | Get statistics for a marketing email. Not available via HubSpot Remote MCP. |
177
+ | `reporting_dashboards_get` | reporting | read | migration, full | Get an analytics report/dashboard by ID |
178
+ | `reporting_dashboards_list` | reporting | read | migration, full | List analytics reports/dashboards with pagination |
179
+ | `sales_sequences_enrollments` | sales | read | migration, full | Get enrollments for a sales sequence |
180
+ | `sales_sequences_get` | sales | read | migration, full | Get a sales sequence by ID |
181
+ | `sales_sequences_list` | sales | read | migration, full | List sales sequences with pagination |
182
+ | `search_crm_objects` | compat | read | minimal, crm, migration, full | Search CRM records by filter groups, text query, sorts, and pagination. HubSpot-compat: accepts the same filterGroups/query/sorts/properties/limit/after shape as HubSpot Remote MCP. |
183
+ | `search_owners` | compat | read | minimal, crm, migration, full | Find CRM owners by email or ID, or list all. HubSpot-compat: max 100 results. |
184
+ | `search_properties` | compat | read | minimal, crm, migration, full | Keyword-search property definitions for an object type. HubSpot-compat: max 5 keywords. |
185
+ | `webhooks_create_subscription` | webhooks | write | migration, full | Create a webhook subscription (dry-run by default unless force=true). Not available via HubSpot Remote MCP. |
186
+ | `webhooks_delete_subscription` | webhooks | delete | migration, full | Delete a webhook subscription (dry-run by default unless force=true). Not available via HubSpot Remote MCP. |
187
+ | `webhooks_list_subscriptions` | webhooks | read | migration, full | List webhook subscriptions for a HubSpot app. Not available via HubSpot Remote MCP. |
188
+ | `workflows_enroll` | workflows | write | migration, full | Enroll a contact into a legacy v3 workflow (dry-run by default unless force=true). Not available via HubSpot Remote MCP. |
189
+ | `workflows_get` | workflows | read | migration, full | Get one workflow by flowId (v4). Not available via HubSpot Remote MCP. |
190
+ | `workflows_list` | workflows | read | migration, full | List workflows (v4). Not available via HubSpot Remote MCP. |
191
+ | `workflows_unenroll` | workflows | read | migration, full | Remove a contact from a legacy v3 workflow (dry-run by default unless force=true). Not available via HubSpot Remote MCP. |
192
+
@@ -0,0 +1,115 @@
1
+ # Migration Adapters
2
+
3
+ This page captures the migration gaps that surfaced during a large HubSpot
4
+ portal mirror and how hscli should handle them in future runs.
5
+
6
+ ## Upstreamed into hscli
7
+
8
+ - Read-only profiles now support HubSpot read endpoints that use `POST`
9
+ payloads, such as CRM search, batch-read, lists search, CMS batch-read,
10
+ communication-preference status batch-read, and association batch-read.
11
+ - Standard CRM objects expose batch create/update/upsert/archive flows.
12
+ - Custom object records expose batch read/create/update/archive flows.
13
+ - CRM association replay has `crm associations batch-read` and
14
+ `crm associations batch-create`, including explicit v4 association types.
15
+ - Custom object schema PATCH fails early when the payload contains
16
+ `properties`; use `crm properties batch-create <objectType>` instead.
17
+ - Property batch-create accepts source property dumps and removes common
18
+ migration hazards: HubSpot-defined/read-only definitions, known reserved
19
+ names, blank enum options, empty enums, and label/name collisions.
20
+ - Owner-reference properties keep `referencedObjectType` and automatically add
21
+ `externalOptions: true`, which HubSpot requires for referenced options.
22
+ - Forms can translate legacy v2 payloads into v3 payloads, split oversized
23
+ field groups, preflight missing target properties, and remap consent
24
+ subscription type IDs.
25
+ - Business units can be captured through the internal browser-session endpoint
26
+ when the public settings endpoint is unavailable.
27
+ - Business units can be created through the internal browser-session endpoint
28
+ with `--skip-existing`, which removes the need for DevTools snippets in the
29
+ common sandbox replay path.
30
+ - Permission sets can be listed/read/created/updated/deleted/cloned through the
31
+ internal browser-session endpoint.
32
+ - Permission-set assignments can be captured from app-users payloads and applied
33
+ through the allowlisted Chirp RPC
34
+ `/api/chirp-frontend-app/v1/gateway/com.hubspot.appusers.rpc.PermissionSetAssignmentsRpc/assignUserToPermissionSets?portalId={portalId}`
35
+ with `assignments: [{ userId, permissionSetIds }]`. Use
36
+ `settings permission-sets users assign` for a single user, or
37
+ `settings permission-sets users assign-batch --data <payload>` to replay the
38
+ native `assignments[]` body for one or more users.
39
+ - Permission migration can also capture the atomic role catalog, role
40
+ categories, scope buckets, seat catalog, user seat assignments, and permission
41
+ templates through the `settings permission-sets *` internal adapters.
42
+ - Permission access audit capture can use the app-user audit Chirp RPCs for
43
+ permission-set assignment history, role change history, and per-user access
44
+ audit evidence. Keep these as capture/audit adapters; do not expose them as a
45
+ generic internal RPC passthrough.
46
+ - Full workflow definitions can be captured through
47
+ `/api/automationplatform/v1/hybrid/{flowId}`, including actions, branches,
48
+ triggers, goals, and output metadata.
49
+ - Brand kits, colors, logos, favicons, fonts, and brand voice metadata can be
50
+ captured through `/api/branding/v1/*` internal browser-session endpoints.
51
+ - Drag-and-drop marketing emails can be captured through `/api/cosemail/v1/*`,
52
+ including layout/theme/flex-area/body metadata.
53
+ - New CTA builder placements can be captured through `/api/ctas/v3/placements`.
54
+ - Design Manager templates, themes, modules, and layouts can be captured
55
+ through `/api/designmanager/v1/*`.
56
+ - CMS content v2 pages/templates, site settings, global groups, built-in module
57
+ mappings, and content-portal-copy job status are included in the internal
58
+ backup allowlist for downgrade/migration evidence.
59
+ - Non-email subscription types can be captured through
60
+ `/api/email/public/v1/subscriptions`.
61
+ - Subscription visibility rules, privacy/GDPR settings, and business-unit
62
+ display maps can be captured through allowlisted subscription Chirp RPCs.
63
+ - CRM UI search, CRM GraphQL, object preview, and single-object UI payloads are
64
+ available through the `crm-search` internal capture surface for report/help
65
+ desk/quote/index fidelity checks.
66
+ - List/segment capture now includes list v3, list v2, contacts-list,
67
+ contact-list-management, ILS segment, segments UI, and legacy segment-search
68
+ fallbacks because HubSpot's list APIs are fragmented across portals.
69
+ - Social broadcasts and connected social accounts can be captured through
70
+ `/api/broadcast/v2/*`; ads account/UI metadata is read-only Chirp RPC only.
71
+ - Browser-session adapters share one guarded resolver: `--ui-domain` must be a
72
+ HubSpot app host, and Netscape/JSON cookie exports are filtered to that host
73
+ so unrelated cookies are not forwarded.
74
+ - Browser-session adapters prefer the `hubspotapi-csrf` cookie value for the
75
+ `X-HubSpot-CSRF-hubspotapi` header. This is the modern internal-CSRF pattern;
76
+ `csrf.app` remains accepted for older surfaces.
77
+ - User creates suppress invite/welcome emails by default. Passing
78
+ `--allow-invite-email` is required before hscli will allow payloads such as
79
+ `sendWelcomeEmail:true`.
80
+ - `crm migration id-map apply` remaps local batch payload fields without making
81
+ HubSpot calls, which is the default path for owners, teams, business units,
82
+ custom object type IDs, and other source-to-target IDs.
83
+
84
+ ## Still Adapter-First
85
+
86
+ These surfaces are supported by migration-specific adapters or manual review
87
+ until their endpoint behavior is stable enough to promote to first-class CLI
88
+ commands:
89
+
90
+ - Team-assignment flows, especially when super admins must be left untouched.
91
+ - Team creation/update. The public team API is read-only on many portals, so
92
+ creation stays adapter-first until the internal endpoint contract is proven.
93
+ - Workflow full replay and activation. Capture is now much stronger via the
94
+ hybrid internal endpoint, but a safe replay still needs an adapter that remaps
95
+ action references, strips/replaces send-email actions for sandbox safety,
96
+ lands workflows disabled, and performs a two-pass workflow ID remap.
97
+ - Subscription type replay through internal subscription-definition endpoints
98
+ when public communication-preference definitions are incomplete for
99
+ brand-scoped portals. Non-email channels are now capturable, but channel-safe
100
+ write adapters still need sandbox confirmation.
101
+ - Reports, dashboards, datasets, playbooks, sequences body, and some sales-email
102
+ assets. These are UI-only or internal-only on many portals and should stay
103
+ documented as non-public migration exceptions unless endpoint coverage is
104
+ proven.
105
+ - Static list membership replay for large datasets. Use hscli for list CRUD and
106
+ membership operations, but rely on a migration adapter for batching, source
107
+ filtering, dependency ordering, and explicit skip policies for out-of-scope
108
+ lists.
109
+
110
+ ## Operator Rule
111
+
112
+ Prefer hscli first for every read/write path. If a browser-session/internal
113
+ adapter is required, keep it explicit, scoped to one migration phase, rate
114
+ limited, idempotent, and backed by an id-map or verifier output that hscli can
115
+ consume later.