conduyt-mcp 4.70.0 → 4.71.1

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  MCP server for [Conduyt CRM](https://conduyt.app) — expose your CRM as AI-accessible tools. Covers the core CRM surface (contacts, deals, pipelines, custom fields, automations, messaging, and more); use `conduyt_api_catalog` to discover the full set of 500+ REST endpoints.
4
4
 
5
- ## 173 Tools
5
+ ## 292 Tools
6
6
 
7
7
  ### Discovery
8
8
  | Tool | Description |
@@ -1,7 +1,28 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { ConduytClient } from "../client.js";
3
+ type DisclosureOn = "inbound" | "outbound" | "both";
4
+ type RecordingPolicy = {
5
+ inbound: boolean;
6
+ outbound: boolean;
7
+ disclosure: {
8
+ enabled: boolean;
9
+ on: DisclosureOn;
10
+ };
11
+ consentGreeting: string;
12
+ enabled: boolean;
13
+ };
14
+ /**
15
+ * What a stored policy MEANS, read the way the server reads it (Codex r5): a workspace that never saved the new shape
16
+ * still stores the old one, `{ enabled, consentGreeting }`, which means outbound recording with the disclosure on
17
+ * whenever a wording is set. Handed back raw, an agent auditing consent would read no disclosure at all. `enabled`
18
+ * stays beside `outbound` as the old name of the same switch. null for a value that is not a policy.
19
+ */
20
+ export declare function effectiveRecordingPolicy(raw: unknown): RecordingPolicy | null;
21
+ /** the iPhone line has no inbound calls: what its policy does there, the way the server applies it */
22
+ export declare function iphoneDomain(policy: RecordingPolicy): RecordingPolicy;
3
23
  /**
4
24
  * Smart Dialing policy (GET/PATCH /api/v1/dialer/settings): the account-wide rules every rep's Smart Dialing run
5
25
  * follows. Read needs the dialer scope; update needs the settings scope (admin). (#95/#96, 2026-09-16)
6
26
  */
7
27
  export declare function registerDialerSettingsTools(server: McpServer, client: ConduytClient): void;
28
+ export {};
@@ -1,5 +1,34 @@
1
1
  import { z } from "zod";
2
2
  import { formatResult } from "../client.js";
3
+ import { patchGeneralSettings } from "./lifecycle.js";
4
+ /** the two lines a workspace records calls on: its Twilio lines (settings.recording) and its iPhone line (settings.projectBlueRecording) */
5
+ const RECORDING_KEY = { twilio: "recording", iphone: "projectBlueRecording" };
6
+ const isOn = (v) => v === "inbound" || v === "outbound" || v === "both";
7
+ /**
8
+ * What a stored policy MEANS, read the way the server reads it (Codex r5): a workspace that never saved the new shape
9
+ * still stores the old one, `{ enabled, consentGreeting }`, which means outbound recording with the disclosure on
10
+ * whenever a wording is set. Handed back raw, an agent auditing consent would read no disclosure at all. `enabled`
11
+ * stays beside `outbound` as the old name of the same switch. null for a value that is not a policy.
12
+ */
13
+ export function effectiveRecordingPolicy(raw) {
14
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
15
+ return null;
16
+ const r = raw;
17
+ const greeting = typeof r.consentGreeting === "string" ? r.consentGreeting.trim() : "";
18
+ const newShape = typeof r.inbound === "boolean" || typeof r.outbound === "boolean" || (r.disclosure !== undefined && r.disclosure !== null);
19
+ if (newShape) {
20
+ const d = (r.disclosure && typeof r.disclosure === "object" ? r.disclosure : {});
21
+ const outbound = typeof r.outbound === "boolean" ? r.outbound : r.enabled === true;
22
+ return { inbound: r.inbound === true, outbound, disclosure: { enabled: d.enabled === true, on: isOn(d.on) ? d.on : "outbound" }, consentGreeting: greeting, enabled: outbound };
23
+ }
24
+ const outbound = r.enabled === true;
25
+ return { inbound: false, outbound, disclosure: { enabled: outbound && greeting.length > 0, on: "outbound" }, consentGreeting: greeting, enabled: outbound };
26
+ }
27
+ /** the iPhone line has no inbound calls: what its policy does there, the way the server applies it */
28
+ export function iphoneDomain(policy) {
29
+ const on = policy.disclosure.on;
30
+ return { ...policy, inbound: false, disclosure: on === "inbound" ? { enabled: false, on: "outbound" } : { enabled: policy.disclosure.enabled, on: "outbound" } };
31
+ }
3
32
  /**
4
33
  * Smart Dialing policy (GET/PATCH /api/v1/dialer/settings): the account-wide rules every rep's Smart Dialing run
5
34
  * follows. Read needs the dialer scope; update needs the settings scope (admin). (#95/#96, 2026-09-16)
@@ -20,4 +49,56 @@ export function registerDialerSettingsTools(server, client) {
20
49
  const result = await client.patch("/api/v1/dialer/settings", { autoAdvanceEnabled, pauseSeconds, advanceOn, openLeadOnDial, smartDialCallerId });
21
50
  return formatResult(result);
22
51
  });
52
+ // Call recording policy (the Call recording card of Settings, released 2026-09-30): which calls are recorded and
53
+ // whether callers hear a disclosure. Stored in the general settings (GET/PATCH /api/v1/settings).
54
+ server.tool("conduyt_get_call_recording_settings", "Read the workspace's call recording policy for both kinds of line. twilio = the workspace's Twilio lines: always an object, with everything off when the workspace never set it. Needs a key whose creator may view settings; without it the tool answers with an error instead of a policy. iphone = the iPhone line's OWN policy (outbound only); null = none was written, and the iPhone line then follows the Twilio lines' policy without its inbound part. Each object is the policy as it is APPLIED: inbound, outbound, disclosure { enabled, on: 'inbound' | 'outbound' | 'both' } (whether a recording disclosure is played and on which calls), consentGreeting (what is said; empty = the default sentence) and enabled (the OLD name of the outbound switch: the same value as outbound). A workspace that never saved the new shape is read the way the server reads its old one: outbound recording with the disclosure on whenever a wording is set. The workspace is responsible for consent where it operates.", {}, async () => {
55
+ const result = (await client.get("/api/v1/settings"));
56
+ const raw = result?.data ?? {};
57
+ const twilio = effectiveRecordingPolicy(raw[RECORDING_KEY.twilio]);
58
+ // a key whose creator may not view settings gets a short answer WITHOUT the policy: that is "not visible",
59
+ // never "not configured" (the full answer always carries the Twilio policy)
60
+ if (!twilio) {
61
+ return { content: [{ type: "text", text: "The call recording policy is not visible to this API key: its creator needs permission to view settings. Nothing can be said about whether calls are recorded." }], isError: true };
62
+ }
63
+ const iphone = effectiveRecordingPolicy(raw[RECORDING_KEY.iphone]);
64
+ return formatResult({ twilio, iphone: iphone ? iphoneDomain(iphone) : null });
65
+ });
66
+ server.tool("conduyt_update_call_recording_settings", "Change the workspace's call recording policy (admin: settings scope). line: 'twilio' (default) or 'iphone'. Send only what changes; what is not sent stays as it is. inbound / outbound: record calls the team receives / makes. disclosureEnabled + disclosureOn ('inbound' | 'outbound' | 'both'): play a recording disclosure and on which calls (send both together). On the iPhone line the disclosure plays on outbound calls only: 'inbound' is refused and 'both' is saved as 'outbound'. consentGreeting: the sentence that is said (500 characters at most). enabled: the OLD name of the outbound switch, accepted for older callers and sent as outbound (when both are sent, outbound decides); nothing else changes with it. Read conduyt_get_call_recording_settings first. The workspace is responsible for consent where it operates.", {
67
+ line: z.enum(["twilio", "iphone"]).optional().describe("Which line's policy: the Twilio lines (default) or the iPhone line"),
68
+ inbound: z.boolean().optional().describe("Record calls the team receives (Twilio lines only)"),
69
+ outbound: z.boolean().optional().describe("Record calls the team makes, including Smart Dialing"),
70
+ enabled: z.boolean().optional().describe("The OLD name of the outbound switch (older callers); prefer outbound"),
71
+ disclosureEnabled: z.boolean().optional().describe("Play a recording disclosure (send together with disclosureOn)"),
72
+ disclosureOn: z.enum(["inbound", "outbound", "both"]).optional().describe("Which calls the disclosure is played on (send together with disclosureEnabled)"),
73
+ consentGreeting: z.string().trim().max(500).optional().describe("What is said; empty string = the default sentence"),
74
+ }, async ({ line, inbound, outbound, enabled, disclosureEnabled, disclosureOn, consentGreeting }) => {
75
+ const fail = (text) => ({ content: [{ type: "text", text }], isError: true });
76
+ if ([inbound, outbound, enabled, disclosureEnabled, disclosureOn, consentGreeting].every((v) => v === undefined))
77
+ return fail("Nothing to change: pass at least one of inbound, outbound, disclosureEnabled + disclosureOn, consentGreeting.");
78
+ if ((disclosureEnabled === undefined) !== (disclosureOn === undefined))
79
+ return fail("disclosureEnabled and disclosureOn are sent together: the disclosure is one setting (whether it plays, and on which calls).");
80
+ const which = line ?? "twilio";
81
+ if (which === "iphone" && inbound !== undefined)
82
+ return fail("The iPhone line records outbound calls only: inbound is not a setting of that line.");
83
+ // the iPhone line has no inbound calls: a disclosure on inbound only would never play, so it is refused here
84
+ // instead of saved as if it were on; 'both' is what the line does on its outbound calls
85
+ if (which === "iphone" && disclosureOn === "inbound")
86
+ return fail("The iPhone line has outbound calls only: a disclosure on inbound calls would never play. Send disclosureOn 'outbound'.");
87
+ const on = which === "iphone" && disclosureOn === "both" ? "outbound" : disclosureOn;
88
+ const policy = {};
89
+ if (inbound !== undefined)
90
+ policy.inbound = inbound;
91
+ // (Codex r4) the old name is the outbound switch and nothing else: sent AS outbound, never as enabled, because
92
+ // the server gives an enabled-only write over a policy saved in the old shape its old whole meaning (the
93
+ // disclosure recomputed from it), which this tool promises not to touch
94
+ if (outbound !== undefined)
95
+ policy.outbound = outbound;
96
+ else if (enabled !== undefined)
97
+ policy.outbound = enabled;
98
+ if (disclosureEnabled !== undefined && on !== undefined)
99
+ policy.disclosure = { enabled: disclosureEnabled, on };
100
+ if (consentGreeting !== undefined)
101
+ policy.consentGreeting = consentGreeting;
102
+ return patchGeneralSettings(client, { [RECORDING_KEY[which]]: policy });
103
+ });
23
104
  }
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "conduyt-mcp",
3
- "version": "4.70.0",
3
+ "version": "4.71.1",
4
+ "mcpName": "com.conduyt/mcp",
4
5
  "description": "MCP server for Conduyt CRM — expose CRM operations as AI-accessible tools",
5
6
  "type": "module",
6
7
  "main": "dist/index.js",
@@ -41,10 +42,10 @@
41
42
  "homepage": "https://conduyt.app",
42
43
  "repository": {
43
44
  "type": "git",
44
- "url": "git+https://github.com/ptaramona/conduyt-mcp.git"
45
+ "url": "git+https://github.com/ptaramona/conduyt-mcp-server.git"
45
46
  },
46
47
  "bugs": {
47
- "url": "https://github.com/ptaramona/conduyt-mcp/issues"
48
+ "url": "https://github.com/ptaramona/conduyt-mcp-server/issues"
48
49
  },
49
50
  "license": "MIT",
50
51
  "author": "Conduyt",
@@ -1,7 +1,7 @@
1
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).",
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 — 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
3
  "statuses": {
4
- "excluded": "never MCP-callable by design \u2014 the reason says why",
4
+ "excluded": "never MCP-callable by design — the reason says why",
5
5
  "planned": "callable in principle; a typed tool is wanted but not built yet"
6
6
  },
7
7
  "rules": [
@@ -43,22 +43,22 @@
43
43
  {
44
44
  "match": "/api/public/",
45
45
  "status": "excluded",
46
- "reason": "public unauthenticated surfaces (forms, booking, unsubscribe) \u2014 no key semantics"
46
+ "reason": "public unauthenticated surfaces (forms, booking, unsubscribe) — no key semantics"
47
47
  },
48
48
  {
49
49
  "match": "/api/v1/public/",
50
50
  "status": "excluded",
51
- "reason": "public unauthenticated surfaces \u2014 no key semantics"
51
+ "reason": "public unauthenticated surfaces — no key semantics"
52
52
  },
53
53
  {
54
54
  "match": "/api/v1/webhooks/manage",
55
55
  "status": "planned",
56
- "reason": "OUTBOUND webhook subscription management (CRUD/test/deliveries) \u2014 agent-relevant, unlike the inbound receivers below"
56
+ "reason": "OUTBOUND webhook subscription management (CRUD/test/deliveries) — agent-relevant, unlike the inbound receivers below"
57
57
  },
58
58
  {
59
59
  "match": "/api/v1/webhooks/replay",
60
60
  "status": "planned",
61
- "reason": "outbound webhook delivery replay \u2014 agent-relevant"
61
+ "reason": "outbound webhook delivery replay — agent-relevant"
62
62
  },
63
63
  {
64
64
  "match": "/api/v1/webhooks/",
@@ -78,7 +78,7 @@
78
78
  {
79
79
  "match": "/api/v1/dialer/",
80
80
  "status": "excluded",
81
- "reason": "realtime browser dialer (WebRTC tokens, live call control) \u2014 needs a live audio session"
81
+ "reason": "realtime browser dialer (WebRTC tokens, live call control) — needs a live audio session"
82
82
  },
83
83
  {
84
84
  "match": "/api/v1/screen-share",
@@ -208,7 +208,7 @@
208
208
  {
209
209
  "match": "/api/v1/email-domains",
210
210
  "status": "planned",
211
- "reason": "sending-domain management (reply-capture subpaths ARE covered \u2014 see the reply_capture_* tools)"
211
+ "reason": "sending-domain management (reply-capture subpaths ARE covered — see the reply_capture_* tools)"
212
212
  },
213
213
  {
214
214
  "match": "/api/v1/mailbox",
@@ -403,7 +403,7 @@
403
403
  {
404
404
  "match": "/api/track/",
405
405
  "status": "excluded",
406
- "reason": "email open/click tracking pixels and redirects \u2014 recipient-facing"
406
+ "reason": "email open/click tracking pixels and redirects — recipient-facing"
407
407
  },
408
408
  {
409
409
  "match": "/api/webhooks/",
@@ -508,7 +508,7 @@
508
508
  {
509
509
  "match": "/api/v1/automation-folders",
510
510
  "status": "planned",
511
- "reason": "automation-folder organization (shipped 2026-08) \u2014 agent-relevant workflow housekeeping; typed tools wanted"
511
+ "reason": "automation-folder organization (shipped 2026-08) — agent-relevant workflow housekeeping; typed tools wanted"
512
512
  },
513
513
  {
514
514
  "match": "POST /api/v1/call-flows/audio",
@@ -518,7 +518,37 @@
518
518
  {
519
519
  "match": "GET /api/v1/ops/capacity",
520
520
  "status": "planned",
521
- "reason": "operator capacity dashboard (#51, shipped 2026-09) \u2014 read-only queue/park telemetry; a typed conduyt_capacity_snapshot tool is agent-relevant for throughput questions"
521
+ "reason": "operator capacity dashboard (#51, shipped 2026-09) — read-only queue/park telemetry; a typed conduyt_capacity_snapshot tool is agent-relevant for throughput questions"
522
+ },
523
+ {
524
+ "match": "GET /api/v1/help/guides",
525
+ "status": "planned",
526
+ "reason": "the Help center's guides (2026-09-30): a typed read tool is next, right after the API is live"
527
+ },
528
+ {
529
+ "match": "GET /api/v1/help/guides/:slug",
530
+ "status": "planned",
531
+ "reason": "one guide's text (2026-09-30): a typed read tool is next, right after the API is live"
532
+ },
533
+ {
534
+ "match": "/api/v1/help/guides/:slug/feedback",
535
+ "status": "excluded",
536
+ "reason": "a person's own answer to Was this helpful: session only (assertSessionOnly), never an API key's"
537
+ },
538
+ {
539
+ "match": "POST /api/v1/help/guides/:slug/open",
540
+ "status": "excluded",
541
+ "reason": "a person's own reading of a guide, for Most read: session only (assertSessionOnly)"
542
+ },
543
+ {
544
+ "match": "POST /api/v1/help/suggestions",
545
+ "status": "excluded",
546
+ "reason": "a person's own suggestion, mailed to support: session only (assertSessionOnly)"
547
+ },
548
+ {
549
+ "match": "/api/v1/support/",
550
+ "status": "excluded",
551
+ "reason": "the Contact support form and its attachment upload: a signed-in person's request, session only (assertSessionOnly)"
522
552
  }
523
553
  ]
524
- }
554
+ }
@@ -1,11 +1,11 @@
1
1
  {
2
- "generatedAt": "2026-09-27T22:23:01.252Z",
3
- "toolCount": 290,
4
- "totalEndpoints": 1015,
2
+ "generatedAt": "2026-09-30T16:40:04.197Z",
3
+ "toolCount": 292,
4
+ "totalEndpoints": 1031,
5
5
  "counts": {
6
6
  "covered": 266,
7
- "excluded": 299,
8
- "planned": 450,
7
+ "excluded": 311,
8
+ "planned": 454,
9
9
  "uncategorized": 0
10
10
  },
11
11
  "matrix": {
@@ -1657,6 +1657,10 @@
1657
1657
  "status": "excluded",
1658
1658
  "reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
1659
1659
  },
1660
+ "GET /api/v1/cron/lead-pool-push": {
1661
+ "status": "excluded",
1662
+ "reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
1663
+ },
1660
1664
  "GET /api/v1/cron/lead-redistribution": {
1661
1665
  "status": "excluded",
1662
1666
  "reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
@@ -1797,6 +1801,10 @@
1797
1801
  "status": "excluded",
1798
1802
  "reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
1799
1803
  },
1804
+ "GET /api/v1/cron/process-subscriber-deals": {
1805
+ "status": "excluded",
1806
+ "reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
1807
+ },
1800
1808
  "GET /api/v1/cron/process-trial-signup-alerts": {
1801
1809
  "status": "excluded",
1802
1810
  "reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
@@ -1805,6 +1813,10 @@
1805
1813
  "status": "excluded",
1806
1814
  "reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
1807
1815
  },
1816
+ "GET /api/v1/cron/process-unsubscribe-intents": {
1817
+ "status": "excluded",
1818
+ "reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
1819
+ },
1808
1820
  "GET /api/v1/cron/process-wake-jobs": {
1809
1821
  "status": "excluded",
1810
1822
  "reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
@@ -2205,6 +2217,10 @@
2205
2217
  "status": "excluded",
2206
2218
  "reason": "realtime browser dialer (WebRTC tokens, live call control) — needs a live audio session"
2207
2219
  },
2220
+ "GET /api/v1/dialer/call/:id": {
2221
+ "status": "excluded",
2222
+ "reason": "realtime browser dialer (WebRTC tokens, live call control) — needs a live audio session"
2223
+ },
2208
2224
  "PATCH /api/v1/dialer/call/:id": {
2209
2225
  "status": "excluded",
2210
2226
  "reason": "realtime browser dialer (WebRTC tokens, live call control) — needs a live audio session"
@@ -2771,6 +2787,30 @@
2771
2787
  "status": "excluded",
2772
2788
  "reason": "unauthenticated liveness probe"
2773
2789
  },
2790
+ "GET /api/v1/help/guides": {
2791
+ "status": "planned",
2792
+ "reason": "the Help center's guides (2026-09-30): a typed read tool is next, right after the API is live"
2793
+ },
2794
+ "GET /api/v1/help/guides/:slug": {
2795
+ "status": "planned",
2796
+ "reason": "the Help center's guides (2026-09-30): a typed read tool is next, right after the API is live"
2797
+ },
2798
+ "GET /api/v1/help/guides/:slug/feedback": {
2799
+ "status": "planned",
2800
+ "reason": "the Help center's guides (2026-09-30): a typed read tool is next, right after the API is live"
2801
+ },
2802
+ "POST /api/v1/help/guides/:slug/feedback": {
2803
+ "status": "excluded",
2804
+ "reason": "a person's own answer to Was this helpful: session only (assertSessionOnly), never an API key's"
2805
+ },
2806
+ "POST /api/v1/help/guides/:slug/open": {
2807
+ "status": "excluded",
2808
+ "reason": "a person's own reading of a guide, for Most read: session only (assertSessionOnly)"
2809
+ },
2810
+ "POST /api/v1/help/suggestions": {
2811
+ "status": "excluded",
2812
+ "reason": "a person's own suggestion, mailed to support: session only (assertSessionOnly)"
2813
+ },
2774
2814
  "GET /api/v1/imports": {
2775
2815
  "status": "planned",
2776
2816
  "reason": "import job monitoring/resubmission (ai-import tool covers creation)"
@@ -3311,6 +3351,10 @@
3311
3351
  "status": "excluded",
3312
3352
  "reason": "public unauthenticated surfaces — no key semantics"
3313
3353
  },
3354
+ "POST /api/v1/public/screen-share/:code/state": {
3355
+ "status": "excluded",
3356
+ "reason": "public unauthenticated surfaces — no key semantics"
3357
+ },
3314
3358
  "POST /api/v1/public/subscribe": {
3315
3359
  "status": "excluded",
3316
3360
  "reason": "public unauthenticated surfaces — no key semantics"
@@ -3727,6 +3771,10 @@
3727
3771
  "status": "excluded",
3728
3772
  "reason": "realtime browser RTC surface"
3729
3773
  },
3774
+ "GET /api/v1/screen-share/session/:code": {
3775
+ "status": "excluded",
3776
+ "reason": "realtime browser RTC surface"
3777
+ },
3730
3778
  "DELETE /api/v1/screen-share/session/:code": {
3731
3779
  "status": "excluded",
3732
3780
  "reason": "realtime browser RTC surface"
@@ -3742,6 +3790,7 @@
3742
3790
  "tools": [
3743
3791
  "conduyt_get_contact_table_defaults",
3744
3792
  "conduyt_set_contact_table_defaults",
3793
+ "conduyt_get_call_recording_settings",
3745
3794
  "conduyt_get_lead_pool_settings",
3746
3795
  "conduyt_update_lead_pool_settings",
3747
3796
  "conduyt_get_lifecycle_settings",
@@ -4195,6 +4244,14 @@
4195
4244
  "status": "excluded",
4196
4245
  "reason": "UI enum/metadata hydration for dashboard pickers; tools embed their enums"
4197
4246
  },
4247
+ "POST /api/v1/support/requests": {
4248
+ "status": "excluded",
4249
+ "reason": "the Contact support form and its attachment upload: a signed-in person's request, session only (assertSessionOnly)"
4250
+ },
4251
+ "POST /api/v1/support/upload-url": {
4252
+ "status": "excluded",
4253
+ "reason": "the Contact support form and its attachment upload: a signed-in person's request, session only (assertSessionOnly)"
4254
+ },
4198
4255
  "GET /api/v1/tags": {
4199
4256
  "status": "covered",
4200
4257
  "tools": [
@@ -4400,7 +4457,11 @@
4400
4457
  "status": "planned",
4401
4458
  "reason": "team management subroutes beyond list/invite"
4402
4459
  },
4403
- "POST /api/v1/users/me/welcomed": {
4460
+ "GET /api/v1/users/me/tour": {
4461
+ "status": "planned",
4462
+ "reason": "team management subroutes beyond list/invite"
4463
+ },
4464
+ "POST /api/v1/users/me/tour": {
4404
4465
  "status": "planned",
4405
4466
  "reason": "team management subroutes beyond list/invite"
4406
4467
  },
@@ -4558,6 +4619,10 @@
4558
4619
  "status": "excluded",
4559
4620
  "reason": "inbound provider callback receivers (Twilio/Resend/etc.), not client-callable"
4560
4621
  },
4622
+ "POST /api/v1/webhooks/voice/vendor-greeting": {
4623
+ "status": "excluded",
4624
+ "reason": "inbound provider callback receivers (Twilio/Resend/etc.), not client-callable"
4625
+ },
4561
4626
  "POST /api/v1/webhooks/voice/vendor-recording": {
4562
4627
  "status": "excluded",
4563
4628
  "reason": "inbound provider callback receivers (Twilio/Resend/etc.), not client-callable"