@revfleet/hscli 0.8.7 → 0.8.10

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 (75) hide show
  1. package/CHANGELOG.md +204 -0
  2. package/CONTRIBUTING.md +120 -0
  3. package/README.md +14 -6
  4. package/brand/readme-hero.svg +25 -0
  5. package/dist/cli.js +10 -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 +32 -6
  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/shared.d.ts +1 -0
  22. package/dist/commands/crm/shared.js +3 -4
  23. package/dist/commands/crm/shared.js.map +1 -1
  24. package/dist/commands/events/index.js +7 -8
  25. package/dist/commands/events/index.js.map +1 -1
  26. package/dist/commands/marketing/index.js +3 -4
  27. package/dist/commands/marketing/index.js.map +1 -1
  28. package/dist/commands/settings/index.js +12 -13
  29. package/dist/commands/settings/index.js.map +1 -1
  30. package/dist/core/http.d.ts +27 -0
  31. package/dist/core/http.js +87 -18
  32. package/dist/core/http.js.map +1 -1
  33. package/dist/core/plugins.d.ts +5 -2
  34. package/dist/core/plugins.js +18 -1
  35. package/dist/core/plugins.js.map +1 -1
  36. package/dist/core/telemetry-context.d.ts +13 -0
  37. package/dist/core/telemetry-context.js +30 -0
  38. package/dist/core/telemetry-context.js.map +1 -0
  39. package/dist/mcp/hubspot-modules.d.ts +30 -0
  40. package/dist/mcp/hubspot-modules.js +305 -0
  41. package/dist/mcp/hubspot-modules.js.map +1 -0
  42. package/dist/mcp/server.d.ts +2 -0
  43. package/dist/mcp/server.js +30 -26
  44. package/dist/mcp/server.js.map +1 -1
  45. package/docs/ARCHITECTURE.md +39 -0
  46. package/docs/CAPABILITY_LIBRARY.md +638 -0
  47. package/docs/CMS_SETUP.md +349 -0
  48. package/docs/COMMAND_COMPATIBILITY.md +24 -0
  49. package/docs/COMMAND_TREE.md +183 -0
  50. package/docs/COMMERCE_SETUP.md +400 -0
  51. package/docs/COMPARISON.md +146 -0
  52. package/docs/COOKBOOK.md +800 -0
  53. package/docs/INTEGRATIONS_NOTIFICATIONS_SETUP.md +352 -0
  54. package/docs/MARKETING_SETUP.md +503 -0
  55. package/docs/MCP.md +171 -0
  56. package/docs/OPERATIONAL_PLAYBOOKS.md +322 -0
  57. package/docs/OPERATIONS_SETUP.md +362 -0
  58. package/docs/PLUGIN_GUIDE.md +158 -0
  59. package/docs/POLICY_EXAMPLE.json +57 -0
  60. package/docs/PORTAL_SETUP.md +683 -0
  61. package/docs/PUBLISHING.md +154 -0
  62. package/docs/RELEASE_GOVERNANCE.md +34 -0
  63. package/docs/REPORTING_SETUP.md +310 -0
  64. package/docs/ROADMAP-DATE-BASED-API.md +103 -0
  65. package/docs/ROADMAP_PHASE1_TO_3.md +96 -0
  66. package/docs/SAFETY_MODEL.md +37 -0
  67. package/docs/SALES_SETUP.md +369 -0
  68. package/docs/SERVICE_SETUP.md +403 -0
  69. package/docs/TESTING_PLAN.md +89 -0
  70. package/docs/TIERS.md +320 -0
  71. package/docs/TUTORIALS/audit-portal-writes.md +150 -0
  72. package/docs/TUTORIALS/secure-agent-writes.md +177 -0
  73. package/docs/TUTORIALS/trace-replay-repro.md +147 -0
  74. package/docs/WHY_HOW_WHAT.md +81 -0
  75. package/package.json +7 -2
package/docs/TIERS.md ADDED
@@ -0,0 +1,320 @@
1
+ # HubSpot Plan Tier Requirements
2
+
3
+ Every HubSpot public API endpoint is gated by a specific HubSpot plan tier. hscli
4
+ exposes **100% of the 1180 public endpoints** as CLI commands, but whether a
5
+ given command actually returns data depends on whether the portal has the
6
+ required tier activated.
7
+
8
+ This document maps hscli modules to the HubSpot product hubs and tiers that
9
+ unlock them, based on [HubSpot's official pricing page](https://www.hubspot.com/pricing)
10
+ and the [developer documentation](https://developers.hubspot.com/docs/api/overview)
11
+ as of April 2026. Tiers are cumulative — e.g. "Marketing Hub Professional+" means
12
+ Professional and Enterprise both unlock the endpoint.
13
+
14
+ If an endpoint returns **403 Forbidden** with a message like *"This endpoint is
15
+ not available on your account"* or *"This feature is part of HubSpot's [X] Hub"*,
16
+ it's tier-locked. Upgrade the portal to the required tier or test on a portal
17
+ that has it.
18
+
19
+ ## Quick tier map
20
+
21
+ | Tier | Reachable endpoints (approx) |
22
+ |---|---:|
23
+ | **Free** (any account) | ~550 |
24
+ | **Starter** (any hub, $20-50/mo) | +~80 |
25
+ | **Professional** (any hub, $800-900/mo) | +~250 |
26
+ | **Enterprise** (any hub, $3200+/mo) | +~100 |
27
+ | **Commerce Hub** add-on | +~60 |
28
+ | **Operations Hub Pro+** | +~40 |
29
+ | **Developer Test Account** (free) | +~30 |
30
+ | **Developer App + legacy hapikey** | +~40 (zombie — see below) |
31
+
32
+ Cumulative ceiling with Marketing+Sales+Service+CMS+Ops+Commerce **all at
33
+ Enterprise plus a dev app**: ~1140 / 1180 reachable (96.6%). Of the remaining
34
+ ~40, most are [zombie endpoints](#zombie-endpoints) HubSpot never migrated off
35
+ legacy auth.
36
+
37
+ ---
38
+
39
+ ## Free (no paid plan required)
40
+
41
+ These endpoints work on any HubSpot portal, including free personal accounts
42
+ and Developer Test Accounts.
43
+
44
+ ### CRM objects (standard)
45
+ - `hscli crm contacts ...` — `/crm/v3/objects/contacts` (all CRUD + search + batch)
46
+ - `hscli crm companies ...` — `/crm/v3/objects/companies`
47
+ - `hscli crm deals ...` — `/crm/v3/objects/deals`
48
+ - `hscli crm tickets ...` — `/crm/v3/objects/tickets`
49
+ - `hscli crm engagements` — notes, tasks, calls, meetings, emails
50
+ - `hscli crm feedback-submissions`
51
+ - `hscli crm communications` — SMS / WhatsApp logs
52
+
53
+ ### CRM infrastructure
54
+ - `hscli crm properties ...` — `/crm/v3/properties/{objectType}` + groups
55
+ - `hscli crm pipelines ...` — `/crm/v3/pipelines/{deals|tickets}`
56
+ - `hscli crm owners ...` — `/crm/v3/owners`
57
+ - `hscli crm associations ...` — `/crm/v3/associations` + `/crm/v4/associations`
58
+ - `hscli crm schemas ...` (custom objects) — `/crm/v3/schemas`
59
+ - `hscli crm imports ...` — `/crm/v3/imports`
60
+ - `hscli crm exports ...` — `/crm/v3/exports`
61
+ - `hscli lists ...` + `lists folders` — `/crm/v3/lists`
62
+
63
+ ### Settings (admin)
64
+ - `hscli settings users,teams,currencies` — `/settings/v3/users`, `/settings/v3/users/teams`, `/settings/v3/currencies`
65
+ - `hscli settings audit-logs` — `/settings/v3/audit-logs` (read-only)
66
+ - `hscli account info,api-usage,private-apps,audit-logs` — `/account-info/v3`
67
+
68
+ ### Files + Forms
69
+ - `hscli files ...` + `files folders` — `/files/v3/files`, `/files/v3/folders`
70
+ - `hscli forms ...` — `/marketing/v3/forms` (basic CRUD)
71
+ - `hscli submissions ...` — `/form-integrations/v1`, `/submissions/v3`
72
+
73
+ ### Webhooks
74
+ - `hscli webhooks ...` — `/webhooks/v3/subscriptions` (portal-side)
75
+
76
+ ### OAuth + Auth
77
+ - `hscli auth ...` — `/oauth/v1`, `/oauth/v3`
78
+
79
+ ### Communication Preferences
80
+ - `hscli communication-preferences ...` — `/communication-preferences/v3` + v4 batch
81
+
82
+ ### Events
83
+ - `hscli events ...` — `/events/v3` (behavioral events + event definitions)
84
+ - `hscli timeline ...` — `/crm/v3/timeline/events` (emit events, not templates)
85
+
86
+ ### Conversations (read)
87
+ - `hscli conversations threads,messages,actors` — `/conversations/v3/conversations`
88
+
89
+ ### Legacy CRM v1/v2
90
+ All work on Free portals (backward-compatibility preserved by HubSpot):
91
+ - `hscli contacts-v1` — `/contacts/v1`
92
+ - `hscli companies-v2` — `/companies/v2`
93
+ - `hscli deals-v1` — `/deals/v1`
94
+ - `hscli owners-v2` — `/owners/v2`
95
+ - `hscli engagements-v1` — `/engagements/v1`
96
+ - `hscli properties-legacy` — `/properties/v1`, `/properties/v2`
97
+
98
+ ### Analytics (basic)
99
+ - `hscli reporting` — `/analytics/v2/reports` (read, limited by tier)
100
+
101
+ ---
102
+
103
+ ## Marketing Hub (Starter+)
104
+
105
+ Upgrade: $20/mo and up. Unlocks email marketing + form follow-ups.
106
+
107
+ ### Starter
108
+ - `hscli marketing emails list,get` — read `/marketing/v3/emails` (limited send volume)
109
+ - `hscli marketing subscriptions` — subscription types
110
+ - `hscli marketing-emails-v1 list,get` — read legacy emails
111
+ - `hscli ctas read` — CTA reporting (legacy)
112
+
113
+ ### Professional+ (adds)
114
+ - `hscli marketing emails create,update,stats` — full editorial CRUD + analytics
115
+ - `hscli marketing campaigns` — `/marketing/v3/campaigns`
116
+ - `hscli marketing ads` — `/marketing/v3/ads/{accounts,campaigns}`
117
+ - `hscli marketing social` — `/marketing/v3/social/{accounts,posts}`
118
+ - `hscli marketing seo` — `/marketing/v3/seo` + `/cms/v3/seo/audit`
119
+ - `hscli marketing events` + `attendance` + `participations` — `/marketing/v3/marketing-events`
120
+ - `hscli marketing behavioral-events` — custom behavioral event definitions
121
+ - `hscli marketing transactional single-email-send,smtp-tokens` — `/marketing/v3/transactional`
122
+ - `hscli broadcast ...`, `broadcasts-root` — legacy social broadcast
123
+ - `hscli email-events list,get,campaigns` — `/email/public/v1/events` (legacy per-recipient stream)
124
+ - `hscli marketing-extras ads-events,legacy-email-ab-test`
125
+ - `hscli marketing-emails-v1 create,update,clone,publish,statistics` — full legacy CRUD
126
+
127
+ ### Enterprise (adds)
128
+ - `hscli settings business-units ...`, `hscli business-units` — `/settings/v3/business-units`, `/business-units/v3`
129
+ - `marketing.campaigns.revenue.read` scope — revenue attribution endpoints
130
+
131
+ ---
132
+
133
+ ## Sales Hub (Starter+)
134
+
135
+ Upgrade: $20/mo and up. Unlocks meeting scheduler + basic deal tools.
136
+
137
+ ### Starter
138
+ - `hscli scheduler links-list,link-get` — `/scheduler/v3/meetings/meeting-links` (basic)
139
+ - `hscli sales meetings ...` — `/crm/v3/objects/meetings` (create/link)
140
+
141
+ ### Professional+ (adds)
142
+ - `hscli sales sequences list,get,enrollments,enroll,unenroll` — `/automation/v4/sequences`
143
+ - `hscli scheduler book,reschedule,cancel` — advanced scheduler ops
144
+ - `hscli sales calling` — `/crm/v3/extensions/calling/*` (calling SDK, partly zombie — see below)
145
+ - `hscli crm goals` — `/crm/v3/objects/goal_targets` (basic custom goals in Starter, full forecasting Pro)
146
+ - `hscli crm dated associations-2025-09 usage-report` — high-usage associations report
147
+
148
+ ### Enterprise (adds)
149
+ - `crm.schemas.forecasts.read`, `crm.objects.forecasts.read` — forecast data
150
+ - `crm.extensions_calling_transcripts.read,write` — call transcripts
151
+ - `crm.dealsplits.read_write` — deal splits
152
+ - `sales-email-read` — email templates API
153
+
154
+ ---
155
+
156
+ ## Service Hub (Starter+)
157
+
158
+ Upgrade: $20/mo and up. Unlocks ticket automation + chatflows.
159
+
160
+ ### Starter
161
+ - `hscli service chatflows ...` — `/conversations/v3/chatflows`
162
+ - `hscli service feedback` — `/crm/v3/objects/feedback_submissions`
163
+ - `hscli conversations inboxes,channels,channel-accounts` — `/conversations/v3/conversations/*`
164
+ - `hscli conversations custom-channels` — app-dev custom channels
165
+
166
+ ### Professional+ (adds)
167
+ - `hscli service knowledge-base ...` — `/cms/v3/knowledge_base` (articles, settings)
168
+ - `hscli service pipelines` — `/crm/v3/pipelines/tickets` with > 1 pipeline
169
+ - `hscli conversations messages send` — send messages into threads
170
+
171
+ ### Enterprise (adds)
172
+ - Custom survey types + advanced routing (endpoints read-only on Pro)
173
+
174
+ ---
175
+
176
+ ## CMS Hub (Starter+)
177
+
178
+ Upgrade: $23/mo (CMS Hub Starter). Unlocks HubSpot-hosted website.
179
+
180
+ ### Starter
181
+ - `hscli cms site-pages,pages,landing-pages` — basic CRUD
182
+ - `hscli cms url-redirects` — URL redirects
183
+ - `hscli cms topics` — blog topics
184
+ - `hscli cms audit-logs` — content audit log
185
+ - `hscli cms blog-posts,blogs,blog-authors,blog-tags,blog-settings` — full blog CRUD (Starter onwards)
186
+ - `hscli cms landing-page-folders` — landing page organization
187
+
188
+ ### Professional+ (adds)
189
+ - `hscli cms hubdb ...` — `/cms/v3/hubdb/tables` (HubDB)
190
+ - `hscli cms seo-audit` — SEO audit API
191
+ - `hscli cms domains` — multi-domain listing
192
+ - `hscli cms source-code ...` — theme/module source code API
193
+ - `hscli cms content-v2 ...` — legacy Content API v2 (Pages, Layouts, Templates, Modules)
194
+ - `hscli cms pages ab-test,revisions,multi-language` — A/B testing + multi-language
195
+ - `hscli comments` — `/comments/v3/comments`
196
+
197
+ ### Enterprise (adds)
198
+ - `cms.membership.access_groups.read,write` — CMS membership access groups
199
+ - Multi-brand (via Business Units)
200
+
201
+ ---
202
+
203
+ ## Operations Hub (Starter+)
204
+
205
+ Upgrade: $20/mo and up. Unlocks data sync + custom code.
206
+
207
+ ### Starter
208
+ - `hscli crm imports,exports` — enhanced batch import/export
209
+ - `hscli integration-sync` (integrations) — basic 2-way sync
210
+
211
+ ### Professional+ (adds)
212
+ - `hscli automation workflows` — `/automation/v4/flows` (Workflows)
213
+ - `hscli automation actions ...` — `/automation/v4/actions/{appId}` (custom code actions, requires OAuth dev app)
214
+ - `hscli crm dated *` — dated 2025-09 API with batch
215
+ - `hscli marketing behavioral-events` — custom event definitions (send on Ops Pro+)
216
+
217
+ ### Enterprise (adds)
218
+ - Advanced data quality automation + team-based custom-code permissions
219
+
220
+ ---
221
+
222
+ ## Commerce Hub (Commerce add-on)
223
+
224
+ Commerce Hub is a separate add-on (not a tier). Free Commerce gives limited
225
+ Stripe checkout + quotes; paid unlocks the full suite.
226
+
227
+ ### Commerce Hub (Free with Commerce account)
228
+ - `hscli crm quotes ...` — `/crm/v3/objects/quotes` (basic, requires `hs_language`)
229
+ - `hscli crm products ...` — `/crm/v3/objects/products`
230
+ - `hscli crm line-items ...` — `/crm/v3/objects/line_items`
231
+
232
+ ### Commerce Hub Professional+
233
+ - `hscli crm invoices ...` — `/crm/v3/objects/invoices`
234
+ - `hscli crm subscriptions ...` — `/crm/v3/objects/subscriptions`
235
+ - `hscli crm payments ...` — `/crm/v3/objects/payments`
236
+ - `hscli crm orders,carts,discounts,fees,taxes` — commerce objects
237
+ - `hscli payments-subscriptions get,cancel,pause` — `/payments/subscriptions/v1`
238
+ - `hscli tax list,get,create,update,delete` — `/tax/v3/taxes` (tax rates)
239
+
240
+ ---
241
+
242
+ ## Developer Platform (HubSpot Developer Account, free)
243
+
244
+ These endpoints require a [HubSpot Developer Account](https://developers.hubspot.com/)
245
+ (free, separate from a customer portal). You build an "App" in the developer
246
+ account, install it via OAuth onto a test portal, and use the OAuth
247
+ access_token. hscli supports this via `hscli auth oauth-url` +
248
+ `hscli auth oauth-exchange --profile <devapp>`.
249
+
250
+ ### Accessible via OAuth Developer App
251
+ - `hscli webhooks` — `/webhooks/v3/{appId}/subscriptions` (app developer's webhook subscriptions)
252
+ - `hscli automation actions` — `/automation/v4/actions/{appId}` (custom workflow actions)
253
+ - `hscli integrators timeline-event-templates` — `/integrators/timeline/v3/{appId}/event/templates`
254
+ - `hscli integrations timeline create,batch-create,update,delete` — `/integrations/v1/application/{appId}/timeline`
255
+ - `hscli crm cards ...` — `/crm/v3/extensions/cards/{appId}` (UI Extension cards)
256
+ - `hscli feature-flags ...` — `/feature/flags/v3/{appId}/flags` (app feature flags)
257
+ - `hscli appinstalls uninstall` — `/appinstalls/v3/external/install`
258
+
259
+ ### Developer Test Account
260
+ - `hscli integrations me` — `/integrations/v1/me` (introspect installed app on test portal)
261
+
262
+ ---
263
+
264
+ ## Zombie endpoints
265
+
266
+ HubSpot retired the legacy **Developer API Key (`hapikey=...` query param)** for
267
+ new accounts in June 2023, but never migrated these endpoints to modern auth.
268
+ They require a legacy hapikey that only old accounts still have:
269
+
270
+ - `/crm/v3/extensions/calling/{appId}/settings/*` — Calling Extensions SDK (3 endpoints)
271
+ - `/crm/v3/extensions/cards/dev/{appId}` — UI Extensions dev preview (2 endpoints)
272
+ - `/crm/v3/extensions/videoconferencing/settings/{appId}` — Video Conf SDK (2 endpoints)
273
+ - `/media-bridge/v1/{appId}/properties/*` — Media Bridge (24 endpoints)
274
+ - `/media-bridge/v1/{appId}/schemas,settings` — Media Bridge infra (6 endpoints)
275
+ - `/crm/v3/timeline/event-templates` (write) — app-level templates
276
+ - A few others in `/integrators/timeline/v3/{appId}` write path
277
+
278
+ **Total: ~40 zombie endpoints** (3.4% of the 1180 API surface).
279
+
280
+ hscli exposes CLI commands for all of them — they're documented, typed, and
281
+ available — but they'll return 401/403 with
282
+ *"This API can't be called using an OAuth access token. A valid developer API
283
+ key must be provided in the `hapikey=` query param"* on any portal that no
284
+ longer has a legacy hapikey.
285
+
286
+ If HubSpot eventually migrates these (GitHub tracker: [HubSpot dev community](https://community.hubspot.com/)),
287
+ hscli will immediately work on them — the commands already exist.
288
+
289
+ ---
290
+
291
+ ## Reporting an endpoint not working
292
+
293
+ If you hit an endpoint that should work on your tier but returns an unexpected
294
+ error, open an issue at [Luigi08001/hscli/issues](https://github.com/revfleet/hscli/issues/new)
295
+ with:
296
+
297
+ 1. Your portal's HubSpot plan (Starter / Pro / Enterprise, which Hubs)
298
+ 2. The exact `hscli ...` command you ran
299
+ 3. The error output (redact tokens)
300
+ 4. The HubSpot correlation ID from the response (helps HubSpot debug faster)
301
+
302
+ For zombie endpoints, issues will be tagged `hubspot-platform` and tracked
303
+ separately — they aren't fixable in hscli.
304
+
305
+ ---
306
+
307
+ ## Tier reference: HubSpot official pricing
308
+
309
+ Accurate as of 2026-04-21. Prices in USD, monthly, billed annually:
310
+
311
+ | Hub | Free | Starter | Professional | Enterprise |
312
+ |---|---:|---:|---:|---:|
313
+ | Marketing Hub | ✓ | $20 | $890 | $3,600 |
314
+ | Sales Hub | ✓ | $20 | $90 (seat) | $150 (seat) |
315
+ | Service Hub | ✓ | $20 | $90 (seat) | $150 (seat) |
316
+ | CMS Hub | — | $23 | $400 | $1,500 |
317
+ | Operations Hub | ✓ | $20 | $800 | $2,000 |
318
+ | Commerce Hub | ✓ (limited) | — | $90 (seat) | — |
319
+
320
+ Check [hubspot.com/pricing](https://www.hubspot.com/pricing) for the latest.
@@ -0,0 +1,150 @@
1
+ # Tutorial: Audit every write to your HubSpot portal
2
+
3
+ **Time:** ~10 min
4
+ **Prerequisites:** `hscli` installed, at least one trace file recorded (see the [trace tutorial](trace-replay-repro.md)).
5
+
6
+ You need to answer: "Which agent deleted 40 contacts on Friday?" or "Did any automation write to pipelines outside business hours last quarter?" `hscli audit` rolls up JSONL traces to give you operational auditability without a backend.
7
+
8
+ ## Step 0 — ensure traces exist
9
+
10
+ If you haven't recorded yet, start one session:
11
+
12
+ ```bash
13
+ hscli trace start
14
+ # ... run some commands, let MCP tools run, etc ...
15
+ hscli trace stop
16
+ ```
17
+
18
+ Traces are saved as `~/.revfleet/trace-*.jsonl`. `hscli audit` auto-scans that directory by default — no configuration needed.
19
+
20
+ ## Step 1 — timeline
21
+
22
+ ```bash
23
+ hscli audit timeline --since 24h
24
+ ```
25
+
26
+ Chronological event list across every trace file. Useful for "what happened in the last hour / day / week".
27
+
28
+ Flags:
29
+ - `--since 15m | 24h | 7d` — time window (default: all time)
30
+ - `--limit N` — max events to show (default: 100, shown most-recent-first)
31
+ - `--writes-only` — filter to `POST/PUT/PATCH/DELETE`
32
+ - `[file-or-dir]` — positional arg overrides the default `~/.revfleet/` scan
33
+
34
+ ## Step 2 — who did what
35
+
36
+ For each profile, get a breakdown by method, status, and path root:
37
+
38
+ ```bash
39
+ hscli audit who alice --since 7d
40
+ ```
41
+
42
+ Output shape:
43
+ ```json
44
+ {
45
+ "profile": "alice",
46
+ "totalEvents": 412,
47
+ "reads": 380,
48
+ "writes": 32,
49
+ "byMethod": { "GET": 380, "POST": 20, "PATCH": 8, "DELETE": 4 },
50
+ "byStatus": { "200": 390, "201": 18, "400": 2, "500": 2 },
51
+ "byPathRoot": { "crm": 300, "marketing": 80, "cms": 32 },
52
+ "lastTen": [ ... ]
53
+ }
54
+ ```
55
+
56
+ Great for answering "is profile X chatty or abusive?" or "did their error rate spike?".
57
+
58
+ ## Step 3 — who touched this path
59
+
60
+ Inverse lookup: a given path pattern, who's been hitting it?
61
+
62
+ ```bash
63
+ hscli audit what /crm/v3/objects/deals --since 7d
64
+ ```
65
+
66
+ Substring match — `/deals` catches `/crm/v3/objects/deals`, `/crm/v3/objects/deals/123`, `/crm/v3/objects/deals/batch/read`, etc.
67
+
68
+ Output buckets by profile, MCP tool (if the request came from `hscli mcp`), and method. Also lists `recentWrites` — the last 10 write operations against that path.
69
+
70
+ This is the go-to command for incident response: **"An unexpected delete happened on contacts. Who?"**
71
+
72
+ ```bash
73
+ hscli audit what /crm/v3/objects/contacts --since 24h
74
+ # → byProfile: { "alice": 12, "claude-agent": 3 }
75
+ # → byTool: { "crm-batch-delete": 2 }
76
+ # → recentWrites: [ ... with profile, toolName, timestamp ... ]
77
+ ```
78
+
79
+ ## Step 4 — every write, ever
80
+
81
+ The most security-relevant audit view:
82
+
83
+ ```bash
84
+ hscli audit writes --since 24h
85
+ ```
86
+
87
+ Returns *every* `POST/PUT/PATCH/DELETE` event in the window, counts failed vs successful, and includes a `limit`-capped list. Ideal for daily operator review.
88
+
89
+ ## Step 5 — MCP tool breakdown
90
+
91
+ If you're running `hscli mcp` (exposing tools to Claude, Cursor, etc.), every request is tagged with `toolName`. Roll up per tool:
92
+
93
+ ```bash
94
+ hscli audit by-tool --since 7d
95
+ ```
96
+
97
+ Output:
98
+ ```json
99
+ {
100
+ "totalMcpCalls": 1200,
101
+ "uniqueTools": 28,
102
+ "breakdown": [
103
+ { "tool": "crm-contacts-list", "calls": 450, "writes": 0, "errors": 2, "errorRate": "0.4%", "avgMs": 180, "maxMs": 520 },
104
+ { "tool": "crm-contacts-create", "calls": 120, "writes": 120, "errors": 15, "errorRate": "12.5%", "avgMs": 320, "maxMs": 1800 },
105
+ ...
106
+ ]
107
+ }
108
+ ```
109
+
110
+ Sorted by call count descending. Quick pattern-matching:
111
+
112
+ - Highest-call-count tool → "which tool is the chatty one"
113
+ - Highest error rate → "which tool is the buggy one" or "which tool is hitting a capability boundary"
114
+ - Highest maxMs → "which tool has tail-latency issues"
115
+
116
+ ## Step 6 — combine with `jq` for custom views
117
+
118
+ Every audit command outputs JSON when you pass `--json` (or use `--format json` globally). Pipe into `jq` for ad-hoc queries:
119
+
120
+ ```bash
121
+ # All 5xx writes in the last day, path + profile + ts
122
+ hscli --json audit writes --since 24h \
123
+ | jq '.data.writes[] | select(.status >= 500) | { ts, profile, method, path, status }'
124
+
125
+ # Top 5 profiles by write count
126
+ hscli --json audit timeline --since 7d --writes-only --limit 10000 \
127
+ | jq '.data.events | group_by(.profile) | map({ profile: .[0].profile, count: length }) | sort_by(-.count) | .[:5]'
128
+ ```
129
+
130
+ ## Step 7 — feed into CI / Slack / PagerDuty
131
+
132
+ Because everything is JSON-in / JSON-out, you can:
133
+
134
+ - Run `hscli audit writes --since 1h` as a cron job; if any `failedWrites > 0`, POST to Slack.
135
+ - Gate your deploy pipeline on `hscli audit by-tool --since 1h | jq '.data.breakdown[] | select(.errorRate | rtrimstr("%") | tonumber > 5)'` — fail if any tool's error rate spiked.
136
+ - Archive traces to S3 daily for long-term compliance — the format is append-only JSONL, gzip-friendly.
137
+
138
+ ## What you get
139
+
140
+ - Every trace file in `~/.revfleet/` becomes a queryable, structured audit log — no DB, no service to run.
141
+ - Five pre-built aggregations (timeline, who, what, writes, by-tool) cover 90% of operator questions.
142
+ - Works whether the traffic came from a human at a terminal, a cron job, or an MCP-connected agent.
143
+ - Combined with [policy-as-code](secure-agent-writes.md), you get "what rule fired, against which request, from which tool, at what time" — in aggregate.
144
+
145
+ ## Tips
146
+
147
+ - Trace files retain the HTTP request metadata but not the auth token — safe to share between operators.
148
+ - If you store multiple portals in separate profiles (`hscli --profile prod ...`, `--profile staging ...`), `audit who` naturally segments by portal.
149
+ - For a persistent audit trail, write traces to a shared path (`--out /mnt/shared-audit/trace-$(date +%F-%H%M).jsonl`) and scan with `hscli audit --file /mnt/shared-audit/`.
150
+ - `audit who` and `audit what` return `0` for `totalEvents` when nothing matches — no error, just silence — so they're safe to run in pipelines.
@@ -0,0 +1,177 @@
1
+ # Tutorial: Secure agent writes with policy-as-code
2
+
3
+ **Time:** ~15 min
4
+ **Prerequisites:** `hscli` installed, a HubSpot Private App token authenticated (`hscli auth login --token-stdin`).
5
+
6
+ Agents that write to a production HubSpot portal need guardrails beyond `--force`. This tutorial walks through setting up a policy file so that an MCP-backed agent can read freely, write only to safe paths, require change tickets for risky writes, and be prevented from destructive deletes altogether.
7
+
8
+ ## The problem
9
+
10
+ You've connected Claude (or any MCP client) to `hscli mcp` and want it to operate on your live HubSpot portal. Without policy:
11
+
12
+ - The agent could `POST /crm/v3/objects/contacts/gdpr-delete` with a user-provided ID and permanently delete records.
13
+ - Writes are authorised by *whether `--force` was passed*, not *what the write is*.
14
+ - There's no auditable record of *why* a particular write was allowed.
15
+
16
+ `hscli policy` fixes all three.
17
+
18
+ ## Step 1 — start from a template
19
+
20
+ ```bash
21
+ hscli policy templates list
22
+ ```
23
+
24
+ You should see five built-in templates. For an agent that needs to create and update records but must never delete, start with `no-deletes`:
25
+
26
+ ```bash
27
+ hscli policy templates extract no-deletes --to ./policy.json
28
+ ```
29
+
30
+ Open `policy.json` — you'll see a `version: 2` schema with `profiles.default.rules` that allow `GET/POST/PATCH/PUT` and deny all `DELETE`.
31
+
32
+ ## Step 2 — tighten for your environment
33
+
34
+ Say you want:
35
+ - Writes on CRM objects only — no CMS, no workflows.
36
+ - `gdpr-delete` and batch archive operations always blocked.
37
+ - Any write during business hours only, US/Eastern.
38
+ - Any PATCH or PUT requires a change ticket ID.
39
+
40
+ Edit `policy.json`:
41
+
42
+ ```json
43
+ {
44
+ "version": 2,
45
+ "profiles": {
46
+ "default": {
47
+ "defaultAction": "deny",
48
+ "rules": [
49
+ {
50
+ "name": "allow-reads",
51
+ "match": { "method": "GET", "path": "**" },
52
+ "action": "allow"
53
+ },
54
+ {
55
+ "name": "block-gdpr-delete",
56
+ "match": { "method": "*", "path": "**/gdpr-delete**" },
57
+ "action": "deny"
58
+ },
59
+ {
60
+ "name": "block-batch-archive",
61
+ "match": { "method": "POST", "path": "**/batch/archive" },
62
+ "action": "deny"
63
+ },
64
+ {
65
+ "name": "allow-crm-writes-with-ticket",
66
+ "match": { "method": "POST", "path": "/crm/v3/objects/**" },
67
+ "action": "allow",
68
+ "requireChangeTicket": true,
69
+ "window": { "tz": "US/Eastern", "hours": "09-17", "days": "mon-fri" }
70
+ },
71
+ {
72
+ "name": "allow-crm-updates-with-ticket",
73
+ "match": { "method": "PATCH", "path": "/crm/v3/objects/**" },
74
+ "action": "allow",
75
+ "requireChangeTicket": true,
76
+ "window": { "tz": "US/Eastern", "hours": "09-17", "days": "mon-fri" }
77
+ }
78
+ ]
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ Rules are evaluated **first-match-wins**, so order matters. `defaultAction: "deny"` means any method/path not explicitly matched is blocked — safe by default.
85
+
86
+ ## Step 3 — validate before shipping
87
+
88
+ ```bash
89
+ hscli policy validate ./policy.json
90
+ ```
91
+
92
+ This parses the file and flags structural issues (invalid actions, malformed `window.hours`, rules with no `match` block that would match everything). If `valid: true`, you're good.
93
+
94
+ ## Step 4 — dry-run rule matching
95
+
96
+ Before enforcing, confirm the right rule fires for the requests you expect:
97
+
98
+ ```bash
99
+ hscli --policy-file ./policy.json \
100
+ policy show-matching POST /crm/v3/objects/contacts
101
+ # → matched: allow-crm-writes-with-ticket, effectiveAction: allow
102
+
103
+ hscli --policy-file ./policy.json \
104
+ policy show-matching DELETE /crm/v3/objects/contacts/123
105
+ # → matched: (none), effectiveAction: deny (from defaultAction)
106
+
107
+ hscli --policy-file ./policy.json \
108
+ policy show-matching POST /crm/v3/objects/contacts/gdpr-delete
109
+ # → matched: block-gdpr-delete, effectiveAction: deny
110
+ ```
111
+
112
+ ## Step 5 — enforce
113
+
114
+ Either pass `--policy-file` on every invocation, or set it globally:
115
+
116
+ ```bash
117
+ export HSCLI_POLICY_FILE=$PWD/policy.json
118
+ ```
119
+
120
+ From now on, every `hscli` command (and every MCP tool call) runs through the policy. A denied write fails *before* the HTTP request is issued.
121
+
122
+ Try a denied operation:
123
+
124
+ ```bash
125
+ hscli --force crm contacts delete 123
126
+ # → POLICY_DEFAULT_DENY: No policy rule matched DELETE /crm/v3/objects/contacts/123
127
+ # and profile default action is 'deny'.
128
+ ```
129
+
130
+ Try an allowed write without a ticket:
131
+
132
+ ```bash
133
+ hscli --force crm contacts create --data '{"properties":{"email":"jane@example.com"}}'
134
+ # → POLICY_CHANGE_TICKET_REQUIRED: Policy rule 'allow-crm-writes-with-ticket'
135
+ # requires --change-ticket for POST /crm/v3/objects/contacts.
136
+ ```
137
+
138
+ With a ticket:
139
+
140
+ ```bash
141
+ hscli --force --change-ticket CHG-12345 \
142
+ crm contacts create --data '{"properties":{"email":"jane@example.com"}}'
143
+ ```
144
+
145
+ ## Step 6 — hook into MCP
146
+
147
+ For Claude Desktop / Cursor / any MCP client, set the env var in the server config so the policy binds to the agent's stdio server:
148
+
149
+ ```json
150
+ {
151
+ "mcpServers": {
152
+ "hscli": {
153
+ "command": "hscli",
154
+ "args": ["mcp"],
155
+ "env": {
156
+ "HSCLI_MCP_PROFILE": "default",
157
+ "HSCLI_POLICY_FILE": "/absolute/path/to/policy.json"
158
+ }
159
+ }
160
+ }
161
+ }
162
+ ```
163
+
164
+ Now every tool call the agent makes is filtered through the same rules.
165
+
166
+ ## What you get
167
+
168
+ - Every write the agent attempts is auditable against a versioned policy file.
169
+ - Destructive operations (`gdpr-delete`, batch archive) are *structurally* impossible.
170
+ - Change tickets travel with the request and are written to telemetry.
171
+ - Off-hours writes are rejected — no more 2am surprises.
172
+
173
+ ## Going further
174
+
175
+ - Pair this with `hscli trace start` (see the [trace tutorial](trace-replay-repro.md)) to record every request + matched rule to a JSONL file.
176
+ - Then use [`hscli audit`](audit-portal-writes.md) to answer questions like "all deletes blocked by policy last 7 days" or "which agent tried the most denied writes".
177
+ - Policy error codes (`POLICY_RULE_DENY`, `POLICY_DEFAULT_DENY`, `POLICY_CHANGE_TICKET_REQUIRED`, `POLICY_APPROVAL_REQUIRED`, `POLICY_OUT_OF_WINDOW`) are machine-readable — parse them in CI to fail builds on denied operations.