@revfleet/hscli 0.8.7 → 0.8.11

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 (113) hide show
  1. package/CHANGELOG.md +247 -0
  2. package/CONTRIBUTING.md +120 -0
  3. package/README.md +42 -8
  4. package/brand/readme-hero.svg +25 -0
  5. package/dist/cli.js +35 -0
  6. package/dist/cli.js.map +1 -1
  7. package/dist/commands/account/index.js +5 -6
  8. package/dist/commands/account/index.js.map +1 -1
  9. package/dist/commands/api/index.js +43 -3
  10. package/dist/commands/api/index.js.map +1 -1
  11. package/dist/commands/auth/index.js +74 -13
  12. package/dist/commands/auth/index.js.map +1 -1
  13. package/dist/commands/cms/hubdb.js +31 -0
  14. package/dist/commands/cms/hubdb.js.map +1 -1
  15. package/dist/commands/cms/index.js +27 -0
  16. package/dist/commands/cms/index.js.map +1 -1
  17. package/dist/commands/cms/source-code.js +21 -0
  18. package/dist/commands/cms/source-code.js.map +1 -1
  19. package/dist/commands/communication-preferences/index.js +11 -12
  20. package/dist/commands/communication-preferences/index.js.map +1 -1
  21. package/dist/commands/crm/activities.d.ts +3 -0
  22. package/dist/commands/crm/activities.js +186 -0
  23. package/dist/commands/crm/activities.js.map +1 -0
  24. package/dist/commands/crm/index.js +4 -0
  25. package/dist/commands/crm/index.js.map +1 -1
  26. package/dist/commands/crm/migration.d.ts +3 -0
  27. package/dist/commands/crm/migration.js +155 -0
  28. package/dist/commands/crm/migration.js.map +1 -0
  29. package/dist/commands/crm/properties.js +105 -18
  30. package/dist/commands/crm/properties.js.map +1 -1
  31. package/dist/commands/crm/property-batch.d.ts +34 -0
  32. package/dist/commands/crm/property-batch.js +205 -0
  33. package/dist/commands/crm/property-batch.js.map +1 -0
  34. package/dist/commands/crm/shared.d.ts +1 -0
  35. package/dist/commands/crm/shared.js +3 -4
  36. package/dist/commands/crm/shared.js.map +1 -1
  37. package/dist/commands/doctor/index.js +119 -1
  38. package/dist/commands/doctor/index.js.map +1 -1
  39. package/dist/commands/events/index.js +7 -8
  40. package/dist/commands/events/index.js.map +1 -1
  41. package/dist/commands/forms/index.js +47 -6
  42. package/dist/commands/forms/index.js.map +1 -1
  43. package/dist/commands/forms/legacy-v2.d.ts +6 -0
  44. package/dist/commands/forms/legacy-v2.js +350 -0
  45. package/dist/commands/forms/legacy-v2.js.map +1 -0
  46. package/dist/commands/forms/property-preflight.d.ts +20 -0
  47. package/dist/commands/forms/property-preflight.js +99 -0
  48. package/dist/commands/forms/property-preflight.js.map +1 -0
  49. package/dist/commands/guide/index.d.ts +3 -0
  50. package/dist/commands/guide/index.js +231 -0
  51. package/dist/commands/guide/index.js.map +1 -0
  52. package/dist/commands/marketing/index.js +3 -4
  53. package/dist/commands/marketing/index.js.map +1 -1
  54. package/dist/commands/settings/index.js +249 -14
  55. package/dist/commands/settings/index.js.map +1 -1
  56. package/dist/core/auth.d.ts +7 -1
  57. package/dist/core/auth.js +45 -7
  58. package/dist/core/auth.js.map +1 -1
  59. package/dist/core/http.d.ts +43 -0
  60. package/dist/core/http.js +227 -57
  61. package/dist/core/http.js.map +1 -1
  62. package/dist/core/output.d.ts +2 -0
  63. package/dist/core/output.js +1 -1
  64. package/dist/core/output.js.map +1 -1
  65. package/dist/core/plugins.d.ts +5 -2
  66. package/dist/core/plugins.js +18 -1
  67. package/dist/core/plugins.js.map +1 -1
  68. package/dist/core/scopes.d.ts +29 -0
  69. package/dist/core/scopes.js +415 -0
  70. package/dist/core/scopes.js.map +1 -0
  71. package/dist/core/telemetry-context.d.ts +13 -0
  72. package/dist/core/telemetry-context.js +30 -0
  73. package/dist/core/telemetry-context.js.map +1 -0
  74. package/dist/mcp/ext-tools.d.ts +1 -1
  75. package/dist/mcp/ext-tools.js +52 -1
  76. package/dist/mcp/ext-tools.js.map +1 -1
  77. package/dist/mcp/hubspot-modules.d.ts +30 -0
  78. package/dist/mcp/hubspot-modules.js +305 -0
  79. package/dist/mcp/hubspot-modules.js.map +1 -0
  80. package/dist/mcp/server.d.ts +2 -0
  81. package/dist/mcp/server.js +120 -48
  82. package/dist/mcp/server.js.map +1 -1
  83. package/docs/ARCHITECTURE.md +39 -0
  84. package/docs/CAPABILITY_LIBRARY.md +639 -0
  85. package/docs/CMS_SETUP.md +349 -0
  86. package/docs/COMMAND_COMPATIBILITY.md +24 -0
  87. package/docs/COMMAND_TREE.md +199 -0
  88. package/docs/COMMERCE_SETUP.md +400 -0
  89. package/docs/COMPARISON.md +146 -0
  90. package/docs/COOKBOOK.md +896 -0
  91. package/docs/INTEGRATIONS_NOTIFICATIONS_SETUP.md +352 -0
  92. package/docs/MARKETING_SETUP.md +509 -0
  93. package/docs/MCP.md +172 -0
  94. package/docs/OPERATIONAL_PLAYBOOKS.md +322 -0
  95. package/docs/OPERATIONS_SETUP.md +362 -0
  96. package/docs/PLUGIN_GUIDE.md +158 -0
  97. package/docs/POLICY_EXAMPLE.json +57 -0
  98. package/docs/PORTAL_SETUP.md +684 -0
  99. package/docs/PUBLISHING.md +154 -0
  100. package/docs/RELEASE_GOVERNANCE.md +34 -0
  101. package/docs/REPORTING_SETUP.md +310 -0
  102. package/docs/ROADMAP-DATE-BASED-API.md +103 -0
  103. package/docs/ROADMAP_PHASE1_TO_3.md +96 -0
  104. package/docs/SAFETY_MODEL.md +40 -0
  105. package/docs/SALES_SETUP.md +369 -0
  106. package/docs/SERVICE_SETUP.md +403 -0
  107. package/docs/TESTING_PLAN.md +89 -0
  108. package/docs/TIERS.md +320 -0
  109. package/docs/TUTORIALS/audit-portal-writes.md +150 -0
  110. package/docs/TUTORIALS/secure-agent-writes.md +177 -0
  111. package/docs/TUTORIALS/trace-replay-repro.md +147 -0
  112. package/docs/WHY_HOW_WHAT.md +81 -0
  113. package/package.json +7 -2
@@ -0,0 +1,684 @@
1
+ # Portal Setup Guide
2
+
3
+ How to set up a new HubSpot portal from scratch for use with hscli, in the correct order. Each phase builds on the previous one — skip nothing, follow the sequence.
4
+
5
+ Use the **Setup Checklist** at the bottom to audit an existing portal and find what's missing.
6
+
7
+ ### Related Setup Guides
8
+
9
+ This guide covers the portal foundation. For hub-specific configuration, see:
10
+
11
+ | Guide | Covers |
12
+ |-------|--------|
13
+ | [MARKETING_SETUP.md](MARKETING_SETUP.md) | Email, campaigns, forms, ads, social, SEO, lead scoring, ABM |
14
+ | [SALES_SETUP.md](SALES_SETUP.md) | Pipelines, quotes, meetings, sequences, playbooks, forecasting |
15
+ | [SERVICE_SETUP.md](SERVICE_SETUP.md) | Tickets, knowledge base, customer portal, SLAs, feedback surveys |
16
+ | [COMMERCE_SETUP.md](COMMERCE_SETUP.md) | Products, payments, invoices, subscriptions, tax |
17
+ | [CMS_SETUP.md](CMS_SETUP.md) | Domains, templates, blog, pages, file manager, developer tools |
18
+ | [OPERATIONS_SETUP.md](OPERATIONS_SETUP.md) | Data sync, data quality, datasets, custom objects, imports |
19
+ | [REPORTING_SETUP.md](REPORTING_SETUP.md) | Dashboards, custom reports, attribution, analytics, goals |
20
+ | [INTEGRATIONS_NOTIFICATIONS_SETUP.md](INTEGRATIONS_NOTIFICATIONS_SETUP.md) | Marketplace apps, webhooks, notifications, security, account defaults |
21
+
22
+ > Also see: [COMMAND_TREE.md](COMMAND_TREE.md) for CLI commands to automate portal setup
23
+
24
+ ---
25
+
26
+ ## Phase 1: Account Foundation (UI only)
27
+
28
+ These settings affect everything downstream. Get them right first.
29
+
30
+ ### 1.1 Account Defaults
31
+
32
+ **Where:** Settings > Account Management > Account Defaults > General
33
+
34
+ | Setting | What to configure | Why it matters |
35
+ |---------|-------------------|----------------|
36
+ | Account name | Your company or project name | Appears in emails, quotes, reports |
37
+ | Time zone | Your primary business timezone | Affects workflow triggers, email send times, reporting windows |
38
+ | Fiscal year | Start month (e.g., January–December) | Affects goal tracking, forecasting, fiscal-period reports |
39
+ | Company name | Legal entity name | Used in email footers (CAN-SPAM), quotes, invoices |
40
+ | Company address | Full physical address | Required for email compliance (CAN-SPAM/GDPR) |
41
+ | Company domain | Your main website domain | Used as default for tracking and branding |
42
+
43
+ > **API:** Read-only (`GET /account-info/v3/details`). These must be set in the UI.
44
+
45
+ ### 1.2 Currency
46
+
47
+ **Where:** Settings > Account Defaults > Currency
48
+
49
+ Set your company (home) currency. Add additional currencies if you sell internationally.
50
+
51
+ | Setting | What to configure |
52
+ |---------|-------------------|
53
+ | Company currency | Your primary currency (e.g., USD, EUR, GBP) |
54
+ | Additional currencies | Any secondary currencies + exchange rates |
55
+ | Number format | Regional format (e.g., 1,234.56 vs 1 234,56) |
56
+
57
+ > **API:** `settings.currencies.read/write` scopes. Currencies can be read and managed via API after initial setup.
58
+ >
59
+ > **Important:** Company currency cannot be changed once set. Choose carefully.
60
+
61
+ ### 1.3 Branding
62
+
63
+ **Where:** Settings > Account Defaults > Branding (if available on your plan)
64
+
65
+ | Setting | What to configure |
66
+ |---------|-------------------|
67
+ | Logo | Company logo (used in emails, quotes, chat widget) |
68
+ | Favicon | Browser tab icon for hosted pages |
69
+ | Brand colors | Primary and secondary colors |
70
+ | Fonts | Default fonts for emails and pages |
71
+
72
+ > **API:** UI only. No public API for branding settings.
73
+
74
+ ### 1.4 Privacy & Consent
75
+
76
+ **Where:** Settings > Privacy & Consent
77
+
78
+ | Setting | What to configure |
79
+ |---------|-------------------|
80
+ | GDPR toggle | Enable if you process EU personal data |
81
+ | Consent types | Define legal basis types (legitimate interest, consent, etc.) |
82
+ | Cookie banner | Configure consent banner for tracking |
83
+ | Subscription types | Email opt-in/out categories |
84
+
85
+ > **API:** Partial. Consent properties can be set via API; GDPR mode itself is UI-only.
86
+ >
87
+ > **Why now:** Enabling GDPR after data exists creates retroactive compliance problems. Set it before any data enters the portal.
88
+
89
+ ---
90
+
91
+ ## Phase 2: Domain & Email Setup (UI + DNS)
92
+
93
+ ### 2.1 Connect Your Domain
94
+
95
+ **Where:** Settings > Content > Domains (or via the domain setup wizard)
96
+
97
+ | Domain type | Purpose | Example |
98
+ |-------------|---------|---------|
99
+ | Website domain | Landing pages, website pages | `www.yourcompany.com` |
100
+ | Email sending domain | Authenticated email delivery | `yourcompany.com` |
101
+ | Blog domain | Blog hosting | `blog.yourcompany.com` |
102
+ | Knowledge base | Help center hosting | `help.yourcompany.com` |
103
+
104
+ **Process:**
105
+ 1. Add the domain in HubSpot
106
+ 2. HubSpot provides DNS records (CNAME, TXT)
107
+ 3. Add records at your DNS registrar
108
+ 4. Wait for verification (can take up to 48h, usually minutes)
109
+
110
+ > **API:** Read-only (`GET /cms/v3/domains`). Domain connection requires the UI + DNS configuration.
111
+
112
+ ### 2.2 Email Authentication (DKIM, SPF)
113
+
114
+ **Where:** Settings > Marketing > Email > Configuration
115
+
116
+ | Record | Purpose |
117
+ |--------|---------|
118
+ | CNAME (DKIM) | Proves emails are from your domain, not spoofed |
119
+ | TXT (SPF) | Authorizes HubSpot to send email on your behalf |
120
+ | DMARC | Policy for handling failed authentication (set at your DNS) |
121
+
122
+ HubSpot provides the specific DNS records. Add them at your registrar and verify.
123
+
124
+ > **Why now:** Without email authentication, marketing and transactional emails may land in spam. Must be done before sending any emails.
125
+
126
+ ### 2.3 Tracking Code
127
+
128
+ **Where:** Settings > Tracking & Analytics > Tracking Code
129
+
130
+ Install the HubSpot tracking code on your website:
131
+ - **HubSpot-hosted pages:** Automatic, no action needed
132
+ - **External website:** Copy the JavaScript snippet and add it to your site's `<head>` tag
133
+
134
+ > **API:** The tracking code API can push events, but installing the snippet is manual.
135
+
136
+ ---
137
+
138
+ ## Phase 3: Users & Teams (UI + API)
139
+
140
+ ### 3.1 Invite Users
141
+
142
+ **Where:** Settings > Account Management > Users & Teams > Add users
143
+
144
+ | Setting | What to configure |
145
+ |---------|-------------------|
146
+ | Email addresses | Invite by email |
147
+ | Seats | Assign seat types (Core, Sales, Service, etc.) |
148
+ | Permission set | Super Admin, Admin, or custom permission sets |
149
+
150
+ **Process:**
151
+ 1. Click **Add users** and enter email addresses
152
+ 2. Assign a seat type (determines which tools they access)
153
+ 3. Assign a permission set (determines what they can do)
154
+ 4. Users receive an email invitation and must accept
155
+
156
+ > **API:** Yes — `settings.users.read/write` scopes. The User Provisioning API (`/settings/v3/users`) supports inviting and managing users. Permission sets are available through an internal HubSpot session-auth endpoint in hscli:
157
+ > `hscli settings permission-sets create --portal-id <id> --cookie-file cookies.txt --csrf <token> --data '{...}' --force`.
158
+
159
+ ### 3.2 Create Teams
160
+
161
+ **Where:** Settings > Users & Teams > Teams tab
162
+
163
+ Teams enable:
164
+ - Record ownership segmentation (e.g., "Sales East" vs "Sales West")
165
+ - Team-based reporting and dashboards
166
+ - Permission hierarchies
167
+
168
+ > **API:** Teams are readable via API (`GET /settings/v3/users/teams`). Creation is done in the UI.
169
+
170
+ ### 3.3 Verify Owners
171
+
172
+ Users who can own CRM records appear as "owners." After inviting users, verify they show up:
173
+
174
+ ```bash
175
+ # List all owners in the portal
176
+ hscli crm owners list
177
+
178
+ # Filter by email
179
+ hscli crm owners list --email "user@yourcompany.com"
180
+ ```
181
+
182
+ > **Important:** Note owner IDs — you'll need them for record creation, imports, and bulk operations.
183
+
184
+ ---
185
+
186
+ ## Phase 4: Private App & CLI Authentication (UI + CLI)
187
+
188
+ ### 4.1 Create a Private App
189
+
190
+ **Where:** Settings > Integrations > Private Apps (or Legacy Apps)
191
+
192
+ 1. Click **Create legacy app** (or **Create a private app**)
193
+ 2. Name it (e.g., `hscli`)
194
+ 3. Grant the required scopes:
195
+
196
+ **Minimum scopes for full hscli functionality:**
197
+
198
+ | Category | Scopes |
199
+ |----------|--------|
200
+ | **CRM Objects** | `crm.objects.contacts.read/write`, `crm.objects.companies.read/write`, `crm.objects.deals.read/write`, `crm.objects.deals.sensitive.read` |
201
+ | **CRM Schemas** | `crm.schemas.contacts.read/write`, `crm.schemas.companies.read/write`, `crm.schemas.deals.read/write` |
202
+ | **Tickets** | `tickets`, `tickets.sensitive` |
203
+ | **Pipelines** | `crm.pipelines.orders.read/write` |
204
+ | **Owners** | `crm.objects.owners.read` |
205
+ | **Lists** | `crm.lists.read/write` |
206
+ | **Custom Objects** | `crm.objects.custom.sensitive.read/write` |
207
+ | **Imports** | `crm.import` |
208
+ | **Marketing** | `marketing-email`, `marketing.campaigns.read/write` |
209
+ | **Forms** | `forms` |
210
+ | **Files** | `files` |
211
+ | **CMS** | `content` |
212
+ | **Workflows** | `automation` |
213
+ | **Conversations** | `conversations.read/write` |
214
+ | **Settings** | `settings.users.read/write`, `settings.currencies.read/write` |
215
+
216
+ 4. Click **Create app** and copy the access token
217
+
218
+ **Token format by hublet:**
219
+ - US: `pat-na1-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX`
220
+ - EU: `pat-eu1-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX`
221
+
222
+ ### 4.2 Authenticate hscli
223
+
224
+ ```bash
225
+ # Build hscli (first time only)
226
+ cd hscli-main && npm install && npm run build
227
+
228
+ # Authenticate (pipe to avoid token in shell history)
229
+ printf '%s' 'pat-eu1-XXXX' | hscli auth login --token-stdin
230
+
231
+ # Verify
232
+ hscli auth whoami
233
+ hscli auth token-info
234
+
235
+ # Check capabilities
236
+ hscli doctor capabilities --refresh
237
+ ```
238
+
239
+ **Expected `auth whoami` output:** Portal ID, hublet, uiDomain, authenticated user email.
240
+
241
+ ---
242
+
243
+ ## Phase 5: Data Model — Properties (CLI)
244
+
245
+ Properties define the fields on each CRM object. Standard properties exist by default; add custom properties before importing any data.
246
+
247
+ ### 5.1 Review existing properties
248
+
249
+ ```bash
250
+ # List properties per object type
251
+ hscli crm properties list contacts
252
+ hscli crm properties list companies
253
+ hscli crm properties list deals
254
+ hscli crm properties list tickets
255
+
256
+ # Full schema introspection (includes metadata)
257
+ hscli crm describe contacts
258
+ hscli crm describe deals
259
+ ```
260
+
261
+ ### 5.2 Create custom properties
262
+
263
+ ```bash
264
+ # Example: dropdown property on contacts
265
+ hscli crm properties create contacts --data '{
266
+ "name": "lead_source_detail",
267
+ "label": "Lead Source Detail",
268
+ "type": "enumeration",
269
+ "fieldType": "select",
270
+ "groupName": "contactinformation",
271
+ "options": [
272
+ {"label": "Organic Search", "value": "organic_search", "displayOrder": 0},
273
+ {"label": "Paid Ads", "value": "paid_ads", "displayOrder": 1},
274
+ {"label": "Referral", "value": "referral", "displayOrder": 2}
275
+ ]
276
+ }' --force
277
+
278
+ # Example: number property on deals
279
+ hscli crm properties create deals --data '{
280
+ "name": "mrr",
281
+ "label": "Monthly Recurring Revenue",
282
+ "type": "number",
283
+ "fieldType": "number",
284
+ "groupName": "dealinformation"
285
+ }' --force
286
+ ```
287
+
288
+ > Default behavior is `--dry-run`. Always dry-run first, then add `--force` to commit.
289
+
290
+ ### 5.3 Update existing properties
291
+
292
+ ```bash
293
+ hscli crm properties update contacts --data '{
294
+ "name": "lead_source_detail",
295
+ "label": "Lead Source (Detailed)",
296
+ "options": [
297
+ {"label": "Organic Search", "value": "organic_search", "displayOrder": 0},
298
+ {"label": "Paid Ads", "value": "paid_ads", "displayOrder": 1},
299
+ {"label": "Referral", "value": "referral", "displayOrder": 2},
300
+ {"label": "Partner", "value": "partner", "displayOrder": 3}
301
+ ]
302
+ }' --force
303
+ ```
304
+
305
+ ---
306
+
307
+ ## Phase 6: Data Model — Pipelines (UI + CLI)
308
+
309
+ Pipelines define how deals and tickets flow through your process.
310
+
311
+ ### 6.1 Configure Deal Pipeline
312
+
313
+ **Where:** Settings > Objects > Deals > Pipelines tab
314
+
315
+ Design your stages with probabilities:
316
+
317
+ | Stage | Probability | Meaning |
318
+ |-------|-------------|---------|
319
+ | Prospect | 10% | Initial contact, not yet qualified |
320
+ | Qualification | 30% | Evaluating fit and budget |
321
+ | Proposal | 60% | Proposal sent, awaiting decision |
322
+ | Negotiation | 80% | Terms being discussed |
323
+ | Won | 100% | Deal closed successfully |
324
+ | Lost | 0% | Deal did not close |
325
+
326
+ ### 6.2 Configure Ticket Pipeline
327
+
328
+ **Where:** Settings > Objects > Tickets > Pipelines tab
329
+
330
+ Design your statuses:
331
+
332
+ | Status | Open/Closed |
333
+ |--------|-------------|
334
+ | New | Open |
335
+ | Waiting on contact | Open |
336
+ | Waiting on us | Open |
337
+ | Closed | Closed |
338
+
339
+ ### 6.3 Verify via CLI
340
+
341
+ ```bash
342
+ # List pipelines and their stages
343
+ hscli crm pipelines list deals
344
+ hscli crm pipelines list tickets
345
+
346
+ # Get detailed pipeline info (stage IDs, probabilities)
347
+ hscli crm pipelines get deals
348
+ hscli crm pipelines get tickets
349
+ ```
350
+
351
+ > **Note:** The Pipelines API (`/crm/v3/pipelines`) supports full CRUD. Pipelines can also be created via API if you have the `crm.pipelines.orders.read/write` scope.
352
+
353
+ ---
354
+
355
+ ## Phase 7: Data Model — Custom Objects (CLI, if needed)
356
+
357
+ Custom objects extend the CRM beyond contacts, companies, deals, and tickets.
358
+
359
+ ### 7.1 List existing schemas
360
+
361
+ ```bash
362
+ hscli crm custom-objects schemas list
363
+ ```
364
+
365
+ ### 7.2 Create a custom object
366
+
367
+ ```bash
368
+ hscli crm custom-objects schemas create --data '{
369
+ "name": "project",
370
+ "labels": {"singular": "Project", "plural": "Projects"},
371
+ "primaryDisplayProperty": "project_name",
372
+ "requiredProperties": ["project_name"],
373
+ "properties": [
374
+ {"name": "project_name", "label": "Project Name", "type": "string", "fieldType": "text"},
375
+ {"name": "status", "label": "Status", "type": "enumeration", "fieldType": "select",
376
+ "options": [
377
+ {"label": "Planning", "value": "planning"},
378
+ {"label": "Active", "value": "active"},
379
+ {"label": "Complete", "value": "complete"}
380
+ ]},
381
+ {"name": "budget", "label": "Budget", "type": "number", "fieldType": "number"}
382
+ ],
383
+ "associatedObjects": ["CONTACT", "COMPANY", "DEAL"]
384
+ }' --force
385
+ ```
386
+
387
+ ---
388
+
389
+ ## Phase 8: Test & Validate (CLI)
390
+
391
+ Before importing data at scale, create one record of each type to verify the data model.
392
+
393
+ ### 8.1 Create a test contact
394
+
395
+ ```bash
396
+ # Dry-run first
397
+ hscli crm contacts create --data '{
398
+ "properties": {
399
+ "email": "setup-test@example.com",
400
+ "firstname": "Setup",
401
+ "lastname": "Test",
402
+ "hubspot_owner_id": "<OWNER_ID>"
403
+ }
404
+ }'
405
+
406
+ # Execute
407
+ hscli crm contacts create --data '{
408
+ "properties": {
409
+ "email": "setup-test@example.com",
410
+ "firstname": "Setup",
411
+ "lastname": "Test",
412
+ "hubspot_owner_id": "<OWNER_ID>"
413
+ }
414
+ }' --force
415
+ ```
416
+
417
+ ### 8.2 Create and associate records
418
+
419
+ ```bash
420
+ # Create a company
421
+ hscli crm companies create --data '{
422
+ "properties": {"name": "Test Company", "domain": "testco.com"}
423
+ }' --force
424
+
425
+ # Associate contact → company
426
+ hscli crm associations create contacts <contactId> companies <companyId> --force
427
+
428
+ # Create a deal in the pipeline
429
+ hscli crm deals create --data '{
430
+ "properties": {
431
+ "dealname": "Test Deal",
432
+ "pipeline": "<pipelineId>",
433
+ "dealstage": "<stageId>",
434
+ "amount": "10000",
435
+ "hubspot_owner_id": "<OWNER_ID>"
436
+ }
437
+ }' --force
438
+
439
+ # Associate deal → contact and deal → company
440
+ hscli crm associations create deals <dealId> contacts <contactId> --force
441
+ hscli crm associations create deals <dealId> companies <companyId> --force
442
+ ```
443
+
444
+ ### 8.3 Validate data
445
+
446
+ ```bash
447
+ # Validate a payload against the schema before creating
448
+ hscli crm validate contacts --data '{
449
+ "properties": {"email": "test@example.com", "firstname": "Test"}
450
+ }'
451
+ ```
452
+
453
+ ---
454
+
455
+ ## Phase 9: Data Import (CLI)
456
+
457
+ Once the data model is validated, import your data.
458
+
459
+ ### 9.1 Single records
460
+
461
+ ```bash
462
+ hscli crm contacts create --data '{...}' --force
463
+ hscli crm companies create --data '{...}' --force
464
+ ```
465
+
466
+ ### 9.2 Batch operations
467
+
468
+ ```bash
469
+ hscli crm contacts batch-upsert --data '{
470
+ "inputs": [
471
+ {"properties": {"email": "alice@example.com", "firstname": "Alice"}, "idProperty": "email"},
472
+ {"properties": {"email": "bob@example.com", "firstname": "Bob"}, "idProperty": "email"}
473
+ ]
474
+ }' --force
475
+ ```
476
+
477
+ ### 9.3 CSV imports
478
+
479
+ ```bash
480
+ hscli crm imports create --data '<import-payload>' --force
481
+
482
+ # Check import status
483
+ hscli crm imports list
484
+ hscli crm imports get <importId>
485
+ hscli crm imports errors <importId>
486
+ ```
487
+
488
+ ### Import safety rules
489
+
490
+ - **Validate first:** Create a single test record before bulk operations
491
+ - **5-error hard stop:** If >5 errors on the same endpoint, stop and diagnose
492
+ - **Rate limits:** ~100 requests/10s for private apps. Use sequential processing with 1-2s pauses per 5 records
493
+ - **Idempotent re-runs:** Check for existing records before creating duplicates
494
+ - **Always assign owners:** Never import records without an owner
495
+
496
+ ---
497
+
498
+ ## Phase 10: Engagement Tools (UI + CLI)
499
+
500
+ ### 10.1 Email Configuration
501
+
502
+ **Where:** Settings > Marketing > Email
503
+
504
+ | Setting | What to configure |
505
+ |---------|-------------------|
506
+ | Subscription types | Define email categories (Marketing, Sales, Newsletter, etc.) |
507
+ | Email footer | Company name, address (required by CAN-SPAM) |
508
+ | Default "from" address | noreply@, marketing@, etc. |
509
+
510
+ ### 10.2 Forms
511
+
512
+ ```bash
513
+ # List existing forms
514
+ hscli forms list
515
+
516
+ # Create a form
517
+ hscli forms create --data '{...}' --force
518
+ ```
519
+
520
+ ### 10.3 Workflows
521
+
522
+ **Where:** Automation > Workflows (UI only for building; `automation` scope for custom actions)
523
+
524
+ ```bash
525
+ # List workflows
526
+ hscli workflows flows list
527
+ ```
528
+
529
+ ---
530
+
531
+ ## Setup Checklist
532
+
533
+ Use this to verify a new portal or audit an existing one. Items are in dependency order.
534
+
535
+ ### Phase 1 — Account Foundation (UI)
536
+ ```
537
+ [ ] Account name set
538
+ [ ] Time zone configured
539
+ [ ] Fiscal year set
540
+ [ ] Company name and address filled in (email compliance)
541
+ [ ] Company currency set (cannot be changed later)
542
+ [ ] Additional currencies added (if multi-currency)
543
+ [ ] Branding configured (logo, colors, favicon)
544
+ [ ] Privacy & consent settings enabled (GDPR if applicable)
545
+ ```
546
+
547
+ ### Phase 2 — Domains & Email (UI + DNS)
548
+ ```
549
+ [ ] Website domain connected and verified
550
+ [ ] Email sending domain authenticated (DKIM + SPF)
551
+ [ ] DMARC record configured at DNS registrar
552
+ [ ] Tracking code installed on external website
553
+ ```
554
+
555
+ ### Phase 3 — Users & Teams (UI)
556
+ ```
557
+ [ ] Users invited with correct seats and permissions
558
+ [ ] Teams created (if using team-based segmentation)
559
+ [ ] Owners verified: hscli crm owners list
560
+ ```
561
+
562
+ ### Phase 4 — CLI Authentication
563
+ ```
564
+ [ ] Private App created with required scopes
565
+ [ ] hscli authenticated: hscli auth whoami
566
+ [ ] Token info verified: hscli auth token-info
567
+ [ ] Capabilities checked: hscli doctor capabilities --refresh
568
+ ```
569
+
570
+ ### Phase 5 — Properties (CLI)
571
+ ```
572
+ [ ] Contact properties reviewed/created
573
+ [ ] Company properties reviewed/created
574
+ [ ] Deal properties reviewed/created
575
+ [ ] Ticket properties reviewed/created
576
+ ```
577
+
578
+ ### Phase 6 — Pipelines (UI, verified via CLI)
579
+ ```
580
+ [ ] Deal pipeline stages configured with probabilities
581
+ [ ] Ticket pipeline statuses configured (open/closed)
582
+ [ ] Pipelines verified: hscli crm pipelines list deals
583
+ ```
584
+
585
+ ### Phase 7 — Custom Objects (CLI, if needed)
586
+ ```
587
+ [ ] Custom object schemas created
588
+ [ ] Custom object properties defined
589
+ [ ] Associations configured
590
+ ```
591
+
592
+ ### Phase 8 — Validation (CLI)
593
+ ```
594
+ [ ] Test contact created and verified
595
+ [ ] Test company created and associated
596
+ [ ] Test deal created in correct pipeline/stage
597
+ [ ] Associations verified between objects
598
+ ```
599
+
600
+ ### Phase 9 — Data Import (CLI)
601
+ ```
602
+ [ ] Data imported (contacts, companies, deals, tickets)
603
+ [ ] Import errors reviewed and resolved
604
+ [ ] Owner assignment verified on imported records
605
+ ```
606
+
607
+ ### Phase 10 — Engagement Tools (UI + CLI)
608
+ ```
609
+ [ ] Email subscription types configured
610
+ [ ] Email footer set (company address)
611
+ [ ] Forms created (if needed)
612
+ [ ] Workflows built (if needed)
613
+ ```
614
+
615
+ ---
616
+
617
+ ## Quick Audit Script
618
+
619
+ Run this to quickly assess what's configured in an existing portal:
620
+
621
+ ```bash
622
+ #!/bin/bash
623
+ echo "=== Auth & Connectivity ==="
624
+ hscli auth whoami
625
+ hscli doctor capabilities --refresh
626
+
627
+ echo "=== Owners ==="
628
+ hscli crm owners list
629
+
630
+ echo "=== Pipelines ==="
631
+ hscli crm pipelines list deals
632
+ hscli crm pipelines list tickets
633
+
634
+ echo "=== Property Counts ==="
635
+ hscli crm properties list contacts --json 2>/dev/null | grep -c '"name"' || echo "contacts: error"
636
+ hscli crm properties list companies --json 2>/dev/null | grep -c '"name"' || echo "companies: error"
637
+ hscli crm properties list deals --json 2>/dev/null | grep -c '"name"' || echo "deals: error"
638
+ hscli crm properties list tickets --json 2>/dev/null | grep -c '"name"' || echo "tickets: error"
639
+
640
+ echo "=== Custom Objects ==="
641
+ hscli crm custom-objects schemas list
642
+
643
+ echo "=== Record Counts ==="
644
+ hscli crm contacts search --data '{"filterGroups":[], "limit": 1}' --json 2>/dev/null
645
+ hscli crm companies search --data '{"filterGroups":[], "limit": 1}' --json 2>/dev/null
646
+ hscli crm deals search --data '{"filterGroups":[], "limit": 1}' --json 2>/dev/null
647
+ ```
648
+
649
+ ---
650
+
651
+ ## API vs UI Reference
652
+
653
+ | Step | What | API support | hscli command |
654
+ |------|------|-------------|----------------|
655
+ | Account defaults | Name, timezone, currency | Read-only | — |
656
+ | Branding | Logo, colors, fonts | None | — |
657
+ | Domains | Website, email sending | Read-only | — |
658
+ | Email auth | DKIM, SPF | None (DNS manual) | — |
659
+ | Tracking code | JS snippet | Push events only | — |
660
+ | Privacy/consent | GDPR, cookie banner | Partial | — |
661
+ | Users | Invite, permissions | Full CRUD; permission sets require session auth | `hscli settings users list`, `hscli settings permission-sets list` |
662
+ | Owners | List portal owners | Read | `hscli crm owners list` |
663
+ | Properties | Object fields | Full CRUD | `hscli crm properties list/create/update` |
664
+ | Pipelines | Deal/ticket stages | Full CRUD | `hscli crm pipelines list/get` |
665
+ | Custom objects | Schema + records | Full CRUD | `hscli crm custom-objects schemas list/create` |
666
+ | Data import | Bulk CSV | Full | `hscli crm imports create` |
667
+ | Records | CRUD + search | Full | `hscli crm <object> list/get/create/update/delete` |
668
+ | Associations | Record linking | Full | `hscli crm associations create/list/remove` |
669
+ | Forms | Lead capture | Full CRUD | `hscli forms list/create` |
670
+ | Workflows | Automation | Limited | `hscli workflows flows list` |
671
+ | Marketing | Email, campaigns | Partial | `hscli marketing emails/campaigns list` |
672
+
673
+ ---
674
+
675
+ ## Hublet Reference
676
+
677
+ hscli auto-detects the hublet from the token prefix and routes API calls to the correct endpoint.
678
+
679
+ | Hublet | Token prefix | API base URL | UI domain |
680
+ |--------|-------------|-------------|-----------|
681
+ | US | `pat-na1-*` | `https://api.hubapi.com` | `app.hubspot.com` |
682
+ | EU | `pat-eu1-*` | `https://api-eu1.hubapi.com` | `app-eu1.hubspot.com` |
683
+
684
+ Never hardcode `api.hubapi.com` — it defaults to US and will fail for EU portals.