conduyt-mcp 4.12.0 → 4.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -35,6 +35,7 @@ import { registerDashboardTools } from "./tools/dashboard.js";
35
35
  import { registerAiExtendedTools } from "./tools/ai-extended.js";
36
36
  import { registerWorkflowSandboxTools } from "./tools/workflow-sandbox.js";
37
37
  import { registerPrivacyTools } from "./tools/privacy.js";
38
+ import { registerAccountExportTools } from "./tools/account-exports.js";
38
39
  const apiUrl = process.env.CONDUYT_API_URL;
39
40
  const apiKey = process.env.CONDUYT_API_KEY;
40
41
  if (!apiUrl || !apiKey) {
@@ -92,5 +93,6 @@ registerDashboardTools(server, client);
92
93
  registerAiExtendedTools(server, client);
93
94
  registerWorkflowSandboxTools(server, client);
94
95
  registerPrivacyTools(server, client);
96
+ registerAccountExportTools(server, client);
95
97
  const transport = new StdioServerTransport();
96
98
  await server.connect(transport);
@@ -0,0 +1,14 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { ConduytClient } from "../client.js";
3
+ /**
4
+ * Scheduled account exports (Enterprise): settings + run history.
5
+ *
6
+ * Listing/downloading full-account export files is account-wide PII egress,
7
+ * so the /account-exports endpoints require the EXPLICIT 'account-exports'
8
+ * API-key scope (same posture as 'dnc' — an admin must grant it per key; an
9
+ * unrestricted key does NOT imply it). The settings endpoints ride the
10
+ * normal 'settings' scope. File BYTES are not fetched through MCP (a 50k-row
11
+ * CSV does not belong in a tool result) — tools return metadata plus the
12
+ * authed downloadPath for the API/CLI to retrieve.
13
+ */
14
+ export declare function registerAccountExportTools(server: McpServer, client: ConduytClient): void;
@@ -0,0 +1,35 @@
1
+ import { z } from "zod";
2
+ import { formatResult } from "../client.js";
3
+ /**
4
+ * Scheduled account exports (Enterprise): settings + run history.
5
+ *
6
+ * Listing/downloading full-account export files is account-wide PII egress,
7
+ * so the /account-exports endpoints require the EXPLICIT 'account-exports'
8
+ * API-key scope (same posture as 'dnc' — an admin must grant it per key; an
9
+ * unrestricted key does NOT imply it). The settings endpoints ride the
10
+ * normal 'settings' scope. File BYTES are not fetched through MCP (a 50k-row
11
+ * CSV does not belong in a tool result) — tools return metadata plus the
12
+ * authed downloadPath for the API/CLI to retrieve.
13
+ */
14
+ export function registerAccountExportTools(server, client) {
15
+ server.tool("conduyt_scheduled_export_get_settings", "Get the account's scheduled-export configuration (enabled, frequency, entities) and whether the Enterprise feature is available on the current plan.", {}, async () => {
16
+ const result = await client.get("/api/v1/settings/scheduled-export");
17
+ return formatResult(result);
18
+ });
19
+ server.tool("conduyt_scheduled_export_update_settings", "Update scheduled-export settings. Enabling requires an Enterprise plan (403 upsell otherwise). Omitted fields keep their current values. Weekly exports run on Mondays; all exports run in the morning of the account timezone and files are kept 14 days.", {
20
+ enabled: z.boolean().optional().describe("Turn scheduled exports on/off"),
21
+ frequency: z.enum(["daily", "weekly"]).optional().describe("How often to export (weekly = Mondays)"),
22
+ entities: z
23
+ .array(z.enum(["contacts", "deals", "companies"]))
24
+ .min(1)
25
+ .optional()
26
+ .describe("Which entities to include"),
27
+ }, async (params) => {
28
+ const result = await client.patch("/api/v1/settings/scheduled-export", params);
29
+ return formatResult(result);
30
+ });
31
+ server.tool("conduyt_account_exports_list", "List recent scheduled account-export runs with per-file metadata (rows, truncation, size, expiry) and authed downloadPath for each completed file. Requires the explicit 'account-exports' API-key scope. Download the CSV bytes via the API or CLI using downloadPath — not through MCP.", {}, async () => {
32
+ const result = await client.get("/api/v1/account-exports");
33
+ return formatResult(result);
34
+ });
35
+ }
@@ -1,7 +1,9 @@
1
+ import { createRequire } from "node:module";
1
2
  import { z } from "zod";
2
3
  import { formatResult } from "../client.js";
4
+ const requireJson = createRequire(import.meta.url);
3
5
  export function registerDiscoveryTools(server, client) {
4
- server.tool("conduyt_api_catalog", "Discover all Conduyt CRM API endpoints across 90+ domains, with full request and response schemas. Call this first to learn what the CRM can do — the response carries the exact, current endpoint counts.", {}, async () => {
6
+ server.tool("conduyt_api_catalog", "Discover all Conduyt CRM API endpoints across 90+ domains, with full request and response schemas. Call this first to learn what the CRM can do — the response carries the exact, current endpoint counts. NOTE: the catalog is the full REST surface; not every endpoint has a dedicated MCP tool. Call conduyt_mcp_coverage to see exactly which endpoints are callable through MCP tools versus API-only.", {}, async () => {
5
7
  const result = await client.get("/api/v1/schema/api-catalog");
6
8
  return formatResult(result);
7
9
  });
@@ -22,4 +24,27 @@ export function registerDiscoveryTools(server, client) {
22
24
  const result = await client.get(`/api/v1/search?${params}`);
23
25
  return formatResult(result);
24
26
  });
27
+ server.tool("conduyt_mcp_coverage", "The REST-to-MCP coverage matrix: which of the catalog's API endpoints are callable through MCP tools (and by which tool), which are deliberately excluded (with reasons — e.g. interactive-only auth/billing, inbound webhook receivers, realtime browser surfaces), and which are planned but not yet wrapped. Use this to know up front whether a capability you discovered in conduyt_api_catalog is executable here, instead of failing mid-task. Ships with the package and matches this release's tools exactly.", {
28
+ status: z
29
+ .enum(["covered", "excluded", "planned"])
30
+ .optional()
31
+ .describe("Only return endpoints with this coverage status"),
32
+ pathPrefix: z.string().optional().describe("Only return endpoints whose path starts with this prefix (e.g. /api/v1/contacts)"),
33
+ }, async ({ status, pathPrefix }) => {
34
+ // Pinned at build/publish time by scripts/verify-coverage.mjs (the
35
+ // prepublish gate fails if any endpoint lacks a designation).
36
+ const matrix = requireJson("../../scripts/coverage-matrix.json");
37
+ let entries = Object.entries(matrix.matrix);
38
+ if (status)
39
+ entries = entries.filter(([, v]) => v.status === status);
40
+ if (pathPrefix)
41
+ entries = entries.filter(([k]) => k.split(" ")[1]?.startsWith(pathPrefix));
42
+ return formatResult({
43
+ generatedAt: matrix.generatedAt,
44
+ toolCount: matrix.toolCount,
45
+ totalEndpoints: matrix.totalEndpoints,
46
+ counts: matrix.counts,
47
+ endpoints: Object.fromEntries(entries),
48
+ });
49
+ });
25
50
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conduyt-mcp",
3
- "version": "4.12.0",
3
+ "version": "4.14.0",
4
4
  "description": "MCP server for Conduyt CRM — expose CRM operations as AI-accessible tools",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -10,6 +10,8 @@
10
10
  },
11
11
  "files": [
12
12
  "dist",
13
+ "scripts/coverage-matrix.json",
14
+ "scripts/coverage-manifest.json",
13
15
  "README.md",
14
16
  ".env.example"
15
17
  ],
@@ -21,8 +23,9 @@
21
23
  "build": "tsc",
22
24
  "typecheck": "tsc --noEmit",
23
25
  "test": "node --import tsx --test \"src/**/*.test.ts\"",
24
- "prepublishOnly": "npm run check:version && npm run test && npm run build && ENFORCE=1 node scripts/verify-contracts.mjs",
26
+ "prepublishOnly": "npm run check:version && npm run test && npm run build && ENFORCE=1 node scripts/verify-contracts.mjs && ENFORCE=1 node scripts/verify-coverage.mjs",
25
27
  "audit:contracts": "node scripts/verify-contracts.mjs",
28
+ "audit:coverage": "node scripts/verify-coverage.mjs",
26
29
  "contracts:refresh": "node scripts/refresh-contracts.mjs",
27
30
  "check:version": "node scripts/check-version-sync.mjs"
28
31
  },
@@ -0,0 +1,504 @@
1
+ {
2
+ "description": "SOL-016: the INTENTIONAL REST->MCP coverage policy. Every catalog endpoint must be either covered by a tool (derived automatically), matched by a rule here, or the coverage gate fails \u2014 silent drift between 'discoverable API' and 'callable MCP' is the failure mode this prevents. Rules are first-match-wins prefix matches on 'METHOD /path' keys (a bare path prefix matches every method).",
3
+ "statuses": {
4
+ "excluded": "never MCP-callable by design \u2014 the reason says why",
5
+ "planned": "callable in principle; a typed tool is wanted but not built yet"
6
+ },
7
+ "rules": [
8
+ {
9
+ "match": "/api/health",
10
+ "status": "excluded",
11
+ "reason": "unauthenticated liveness probe; trivially reachable without a tool"
12
+ },
13
+ {
14
+ "match": "/api/v1/cron/",
15
+ "status": "excluded",
16
+ "reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
17
+ },
18
+ {
19
+ "match": "GET /api/v1/account-exports/:id/files/:entity/download",
20
+ "status": "excluded",
21
+ "reason": "raw CSV bytes (up to 50k rows) do not belong in a tool result; conduyt_account_exports_list returns the authed downloadPath for API/CLI retrieval"
22
+ },
23
+ {
24
+ "match": "/api/v1/auth/",
25
+ "status": "excluded",
26
+ "reason": "interactive session/password/SSO flows; API keys are the MCP auth model"
27
+ },
28
+ {
29
+ "match": "/api/v1/admin/",
30
+ "status": "excluded",
31
+ "reason": "super-admin platform operations (Conduyt staff only)"
32
+ },
33
+ {
34
+ "match": "/api/v1/billing",
35
+ "status": "excluded",
36
+ "reason": "the API itself rejects key-based billing management (interactive session required)"
37
+ },
38
+ {
39
+ "match": "/api/public/",
40
+ "status": "excluded",
41
+ "reason": "public unauthenticated surfaces (forms, booking, unsubscribe) \u2014 no key semantics"
42
+ },
43
+ {
44
+ "match": "/api/v1/public/",
45
+ "status": "excluded",
46
+ "reason": "public unauthenticated surfaces \u2014 no key semantics"
47
+ },
48
+ {
49
+ "match": "/api/v1/webhooks/manage",
50
+ "status": "planned",
51
+ "reason": "OUTBOUND webhook subscription management (CRUD/test/deliveries) \u2014 agent-relevant, unlike the inbound receivers below"
52
+ },
53
+ {
54
+ "match": "/api/v1/webhooks/replay",
55
+ "status": "planned",
56
+ "reason": "outbound webhook delivery replay \u2014 agent-relevant"
57
+ },
58
+ {
59
+ "match": "/api/v1/webhooks/",
60
+ "status": "excluded",
61
+ "reason": "inbound provider callback receivers (Twilio/Resend/etc.), not client-callable"
62
+ },
63
+ {
64
+ "match": "/api/v1/scim/",
65
+ "status": "excluded",
66
+ "reason": "IdP-facing SCIM protocol surface (its own bearer tokens)"
67
+ },
68
+ {
69
+ "match": "/api/v1/sso/",
70
+ "status": "excluded",
71
+ "reason": "SAML/SSO configuration + protocol endpoints; interactive/IdP-facing"
72
+ },
73
+ {
74
+ "match": "/api/v1/dialer/",
75
+ "status": "excluded",
76
+ "reason": "realtime browser dialer (WebRTC tokens, live call control) \u2014 needs a live audio session"
77
+ },
78
+ {
79
+ "match": "/api/v1/screen-share",
80
+ "status": "excluded",
81
+ "reason": "realtime browser RTC surface"
82
+ },
83
+ {
84
+ "match": "/api/v1/remote-assist",
85
+ "status": "excluded",
86
+ "reason": "realtime browser RTC surface"
87
+ },
88
+ {
89
+ "match": "/api/v1/push",
90
+ "status": "excluded",
91
+ "reason": "browser push-notification subscription plumbing"
92
+ },
93
+ {
94
+ "match": "/api/v1/confirm",
95
+ "status": "excluded",
96
+ "reason": "emailed one-click confirmation links (token-in-URL), not key-callable"
97
+ },
98
+ {
99
+ "match": "/api/v1/status-metadata",
100
+ "status": "excluded",
101
+ "reason": "UI enum/metadata hydration for dashboard pickers; tools embed their enums"
102
+ },
103
+ {
104
+ "match": "/api/v1/uploads",
105
+ "status": "excluded",
106
+ "reason": "presigned browser upload handshake; MCP has no file-stream transport today"
107
+ },
108
+ {
109
+ "match": "/api/v1/files",
110
+ "status": "planned",
111
+ "reason": "file listing/metadata is agent-useful; upload stays browser-side"
112
+ },
113
+ {
114
+ "match": "/api/v1/chat",
115
+ "status": "planned",
116
+ "reason": "team chat read/post for agents"
117
+ },
118
+ {
119
+ "match": "/api/v1/settings",
120
+ "status": "planned",
121
+ "reason": "account settings read/update surfaces"
122
+ },
123
+ {
124
+ "match": "/api/v1/imports",
125
+ "status": "planned",
126
+ "reason": "import job monitoring/resubmission (ai-import tool covers creation)"
127
+ },
128
+ {
129
+ "match": "/api/v1/reports",
130
+ "status": "planned",
131
+ "reason": "additional report surfaces beyond ai/insights"
132
+ },
133
+ {
134
+ "match": "/api/v1/reporting",
135
+ "status": "planned",
136
+ "reason": "additional report surfaces beyond ai/insights"
137
+ },
138
+ {
139
+ "match": "/api/v1/calendar",
140
+ "status": "planned",
141
+ "reason": "calendar/booking management beyond current appointment tools"
142
+ },
143
+ {
144
+ "match": "/api/v1/calendars",
145
+ "status": "planned",
146
+ "reason": "calendar connection management"
147
+ },
148
+ {
149
+ "match": "/api/v1/booking-pages",
150
+ "status": "planned",
151
+ "reason": "booking page management"
152
+ },
153
+ {
154
+ "match": "/api/v1/booking-routing-forms",
155
+ "status": "planned",
156
+ "reason": "booking routing form management"
157
+ },
158
+ {
159
+ "match": "/api/v1/automations",
160
+ "status": "planned",
161
+ "reason": "automation subroutes beyond the workflow-builder tools (step-logs detail, node ops)"
162
+ },
163
+ {
164
+ "match": "/api/v1/automation-executions",
165
+ "status": "planned",
166
+ "reason": "execution inspection"
167
+ },
168
+ {
169
+ "match": "/api/v1/contacts",
170
+ "status": "planned",
171
+ "reason": "contact subroutes beyond the core CRUD/search tools"
172
+ },
173
+ {
174
+ "match": "/api/v1/deals",
175
+ "status": "planned",
176
+ "reason": "deal subroutes beyond core CRUD (history, files, links)"
177
+ },
178
+ {
179
+ "match": "/api/v1/companies",
180
+ "status": "planned",
181
+ "reason": "company subroutes beyond core CRUD"
182
+ },
183
+ {
184
+ "match": "/api/v1/users",
185
+ "status": "planned",
186
+ "reason": "team management subroutes beyond list/invite"
187
+ },
188
+ {
189
+ "match": "/api/v1/tasks",
190
+ "status": "planned",
191
+ "reason": "task subroutes beyond core CRUD"
192
+ },
193
+ {
194
+ "match": "/api/v1/workflows",
195
+ "status": "planned",
196
+ "reason": "workflow template surfaces"
197
+ },
198
+ {
199
+ "match": "/api/v1/emails",
200
+ "status": "planned",
201
+ "reason": "email template/sequence management"
202
+ },
203
+ {
204
+ "match": "/api/v1/email-domains",
205
+ "status": "planned",
206
+ "reason": "sending-domain management"
207
+ },
208
+ {
209
+ "match": "/api/v1/mailbox",
210
+ "status": "planned",
211
+ "reason": "mailbox sync surfaces"
212
+ },
213
+ {
214
+ "match": "/api/v1/conversations",
215
+ "status": "planned",
216
+ "reason": "conversation thread surfaces"
217
+ },
218
+ {
219
+ "match": "/api/v1/messages",
220
+ "status": "planned",
221
+ "reason": "message surfaces beyond send tools"
222
+ },
223
+ {
224
+ "match": "/api/v1/sms",
225
+ "status": "planned",
226
+ "reason": "SMS surfaces beyond send tools"
227
+ },
228
+ {
229
+ "match": "/api/v1/sms-providers",
230
+ "status": "planned",
231
+ "reason": "external SMS provider management"
232
+ },
233
+ {
234
+ "match": "/api/v1/drip-campaigns",
235
+ "status": "planned",
236
+ "reason": "drip campaign management"
237
+ },
238
+ {
239
+ "match": "/api/v1/drip-tracks",
240
+ "status": "planned",
241
+ "reason": "drip track management"
242
+ },
243
+ {
244
+ "match": "/api/v1/lead-routing",
245
+ "status": "planned",
246
+ "reason": "lead routing rule management"
247
+ },
248
+ {
249
+ "match": "/api/v1/lead-pool",
250
+ "status": "planned",
251
+ "reason": "lead pool claim surfaces"
252
+ },
253
+ {
254
+ "match": "/api/v1/up-for-grabs",
255
+ "status": "planned",
256
+ "reason": "up-for-grabs claim surfaces"
257
+ },
258
+ {
259
+ "match": "/api/v1/invoices",
260
+ "status": "planned",
261
+ "reason": "invoicing"
262
+ },
263
+ {
264
+ "match": "/api/v1/generated-documents",
265
+ "status": "planned",
266
+ "reason": "document generation/sending"
267
+ },
268
+ {
269
+ "match": "/api/v1/document-templates",
270
+ "status": "planned",
271
+ "reason": "document template management"
272
+ },
273
+ {
274
+ "match": "/api/v1/forms",
275
+ "status": "planned",
276
+ "reason": "form management"
277
+ },
278
+ {
279
+ "match": "/api/v1/notifications",
280
+ "status": "planned",
281
+ "reason": "notification surfaces"
282
+ },
283
+ {
284
+ "match": "/api/v1/notes",
285
+ "status": "planned",
286
+ "reason": "note subroutes beyond create"
287
+ },
288
+ {
289
+ "match": "/api/v1/quick-notes",
290
+ "status": "planned",
291
+ "reason": "quick note surfaces"
292
+ },
293
+ {
294
+ "match": "/api/v1/quick-connects",
295
+ "status": "planned",
296
+ "reason": "quick connect management"
297
+ },
298
+ {
299
+ "match": "/api/v1/groups",
300
+ "status": "planned",
301
+ "reason": "member group management"
302
+ },
303
+ {
304
+ "match": "/api/v1/rep-shifts",
305
+ "status": "planned",
306
+ "reason": "rep shift scheduling"
307
+ },
308
+ {
309
+ "match": "/api/v1/availability",
310
+ "status": "planned",
311
+ "reason": "availability windows"
312
+ },
313
+ {
314
+ "match": "/api/v1/smart-views",
315
+ "status": "planned",
316
+ "reason": "smart view management beyond summaries"
317
+ },
318
+ {
319
+ "match": "/api/v1/smart-lists",
320
+ "status": "planned",
321
+ "reason": "smart list management"
322
+ },
323
+ {
324
+ "match": "/api/v1/custom-fields",
325
+ "status": "planned",
326
+ "reason": "custom field admin beyond read"
327
+ },
328
+ {
329
+ "match": "/api/v1/pipelines",
330
+ "status": "planned",
331
+ "reason": "pipeline subroutes beyond core CRUD"
332
+ },
333
+ {
334
+ "match": "/api/v1/calls",
335
+ "status": "planned",
336
+ "reason": "call subroutes beyond list/log"
337
+ },
338
+ {
339
+ "match": "/api/v1/ai",
340
+ "status": "planned",
341
+ "reason": "AI surfaces beyond chat/insights/tasks tools"
342
+ },
343
+ {
344
+ "match": "/api/v1/api-keys",
345
+ "status": "planned",
346
+ "reason": "API key admin beyond self-check"
347
+ },
348
+ {
349
+ "match": "/api/v1/accounts",
350
+ "status": "planned",
351
+ "reason": "sub-account management"
352
+ },
353
+ {
354
+ "match": "/api/v1/batch-operations",
355
+ "status": "planned",
356
+ "reason": "batch operation monitoring"
357
+ },
358
+ {
359
+ "match": "/api/v1/knowledge",
360
+ "status": "planned",
361
+ "reason": "RAG knowledge-base management"
362
+ },
363
+ {
364
+ "match": "/api/v1/playbooks",
365
+ "status": "planned",
366
+ "reason": "playbook management"
367
+ },
368
+ {
369
+ "match": "/api/v1/playbook-enrollments",
370
+ "status": "planned",
371
+ "reason": "playbook enrollment surfaces"
372
+ },
373
+ {
374
+ "match": "/api/v1/warmy-engine",
375
+ "status": "planned",
376
+ "reason": "email warm-up engine surfaces"
377
+ },
378
+ {
379
+ "match": "/api/v1/integrations",
380
+ "status": "planned",
381
+ "reason": "integration management"
382
+ },
383
+ {
384
+ "match": "/api/v1/schema",
385
+ "status": "planned",
386
+ "reason": "schema/catalog subroutes beyond the catalog tool"
387
+ },
388
+ {
389
+ "match": "/api/v1/errors",
390
+ "status": "planned",
391
+ "reason": "client error ingestion"
392
+ },
393
+ {
394
+ "match": "/api/v1/gdpr-export",
395
+ "status": "planned",
396
+ "reason": "gdpr export alias routes (contact-scoped tools exist)"
397
+ },
398
+ {
399
+ "match": "/api/track/",
400
+ "status": "excluded",
401
+ "reason": "email open/click tracking pixels and redirects \u2014 recipient-facing"
402
+ },
403
+ {
404
+ "match": "/api/webhooks/",
405
+ "status": "excluded",
406
+ "reason": "inbound provider callback receivers (Resend), not client-callable"
407
+ },
408
+ {
409
+ "match": "/api/v1/health",
410
+ "status": "excluded",
411
+ "reason": "unauthenticated liveness probe"
412
+ },
413
+ {
414
+ "match": "/api/v1/appointments",
415
+ "status": "planned",
416
+ "reason": "appointment mutation subroutes beyond booking tools"
417
+ },
418
+ {
419
+ "match": "/api/v1/account/",
420
+ "status": "planned",
421
+ "reason": "account readiness/onboarding surfaces"
422
+ },
423
+ {
424
+ "match": "/api/v1/activities",
425
+ "status": "planned",
426
+ "reason": "activity detail reads beyond the log tools"
427
+ },
428
+ {
429
+ "match": "/api/v1/bulk",
430
+ "status": "planned",
431
+ "reason": "bulk operation discovery/monitoring beyond the typed bulk tools"
432
+ },
433
+ {
434
+ "match": "/api/v1/bulk-operations",
435
+ "status": "planned",
436
+ "reason": "bulk operation monitoring"
437
+ },
438
+ {
439
+ "match": "/api/v1/contact",
440
+ "status": "planned",
441
+ "reason": "singular contact alias route"
442
+ },
443
+ {
444
+ "match": "/api/v1/data-model/",
445
+ "status": "planned",
446
+ "reason": "data-model quality reporting"
447
+ },
448
+ {
449
+ "match": "/api/v1/dnc/",
450
+ "status": "planned",
451
+ "reason": "DNC import beyond the typed DNC tools"
452
+ },
453
+ {
454
+ "match": "/api/v1/drip-enrollments",
455
+ "status": "planned",
456
+ "reason": "drip enrollment timeline inspection"
457
+ },
458
+ {
459
+ "match": "/api/v1/email/",
460
+ "status": "planned",
461
+ "reason": "bulk email send surfaces"
462
+ },
463
+ {
464
+ "match": "/api/v1/phone-numbers",
465
+ "status": "planned",
466
+ "reason": "phone number inventory"
467
+ },
468
+ {
469
+ "match": "/api/v1/products",
470
+ "status": "planned",
471
+ "reason": "product catalog subroutes"
472
+ },
473
+ {
474
+ "match": "/api/v1/saved-filters",
475
+ "status": "planned",
476
+ "reason": "saved filter management"
477
+ },
478
+ {
479
+ "match": "/api/v1/scoring-rules",
480
+ "status": "planned",
481
+ "reason": "scoring rule simulation beyond scoring tools"
482
+ },
483
+ {
484
+ "match": "/api/v1/tags",
485
+ "status": "planned",
486
+ "reason": "tag detail reads beyond list/create tools"
487
+ },
488
+ {
489
+ "match": "/api/v1/team/",
490
+ "status": "planned",
491
+ "reason": "team member listing alias"
492
+ },
493
+ {
494
+ "match": "/api/v1/webhook-logs",
495
+ "status": "planned",
496
+ "reason": "outbound webhook delivery logs"
497
+ },
498
+ {
499
+ "match": "/api/v1/webhooks",
500
+ "status": "planned",
501
+ "reason": "outbound webhook subscription management (distinct from the inbound /webhooks/ receivers)"
502
+ }
503
+ ]
504
+ }