@salesforce/afv-skills 1.45.0 → 1.47.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/package.json +1 -1
- package/skills/agentforce-observe/SKILL.md +32 -4
- package/skills/agentforce-observe/references/ahm-alerts.md +719 -0
- package/skills/automation-flow-generate/SKILL.md +11 -5
- package/skills/consumer-goods-promotion-bo-api-deploy/SKILL.md +275 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/README.md +32 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/SetCommentValue.cls +75 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/SetCommentValue.cls-meta.xml +5 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/interview-answers.json +13 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/copy.json +10 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/create.json +20 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/update.json +16 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/conventions-and-payload-rules.md +273 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/generate-and-wire.md +236 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/reference-example-set-comment-value.md +132 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/smoke-and-verify.md +211 -0
- package/skills/dx-code-analyzer-configure/scripts/validate-config.sh +14 -10
- package/skills/dx-code-analyzer-run/scripts/apply-fixes.js +45 -4
- package/skills/dx-code-analyzer-run/scripts/describe-rule.js +52 -32
- package/skills/dx-devops-project-manage/SKILL.md +197 -0
- package/skills/dx-devops-project-manage/examples/common-workflows.md +197 -0
- package/skills/dx-devops-project-manage/references/cli-commands.md +295 -0
- package/skills/dx-devops-project-manage/scripts/create-project.sh +48 -0
- package/skills/dx-devops-project-manage/scripts/list-projects.sh +51 -0
- package/skills/dx-devops-project-manage/scripts/update-project.sh +96 -0
- package/skills/education-cloud-academic-calendar-generate/SKILL.md +225 -0
- package/skills/education-cloud-academic-calendar-generate/examples/quarter-calendar.json +47 -0
- package/skills/education-cloud-academic-calendar-generate/examples/sample-output.md +57 -0
- package/skills/education-cloud-academic-calendar-generate/examples/semester-calendar.json +54 -0
- package/skills/education-cloud-academic-calendar-generate/references/calendar-systems.md +127 -0
- package/skills/education-cloud-academic-calendar-generate/references/date-validation.md +222 -0
- package/skills/education-cloud-academic-calendar-generate/references/foundation_prerequisites.md +40 -0
- package/skills/education-cloud-academic-calendar-generate/scripts/validate_calendar_dates.py +143 -0
- package/skills/education-cloud-course-catalog-migrate/SKILL.md +321 -0
- package/skills/education-cloud-course-catalog-migrate/references/gotchas-detail.md +16 -0
- package/skills/education-cloud-course-catalog-migrate/references/gotchas.md +16 -0
- package/skills/education-cloud-course-catalog-migrate/references/large-catalog-handling.md +42 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/batch_courses.py +36 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/detect_linked_courses.py +51 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/detect_modality_variants.py +48 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/resolve_api_version.py +43 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/split_course_code.py +39 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/validate_completeness.py +54 -0
- package/skills/education-cloud-multi-campus-configure/references/foundation_prerequisites.md +3 -5
- package/skills/education-cloud-student-recruitment-agent-configure/SKILL.md +177 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/agent-and-subagents.md +151 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/customer-narration.md +34 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/execution-model.md +54 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/flows.md +82 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/grounding.md +199 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/permissions.md +183 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/platform-enablement.md +82 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/prerequisites.md +158 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/routing.md +141 -0
- package/skills/experience-cms-brand-apply/SKILL.md +5 -5
- package/skills/experience-cms-brand-create/SKILL.md +2 -2
- package/skills/experience-cms-content-generate/SKILL.md +1 -0
- package/skills/experience-cms-content-render/SKILL.md +173 -0
- package/skills/experience-cms-content-render/assets/angular/DetailPage.component.ts +25 -0
- package/skills/experience-cms-content-render/assets/angular/MediaRenderer.component.ts +133 -0
- package/skills/experience-cms-content-render/assets/angular/TypeList.component.ts +38 -0
- package/skills/experience-cms-content-render/assets/angular/TypeRenderer.component.ts +90 -0
- package/skills/experience-cms-content-render/assets/angular/cms-content.component.ts +248 -0
- package/skills/experience-cms-content-render/assets/angular/cms-item.service.ts +100 -0
- package/skills/experience-cms-content-render/assets/react/DetailPage.tsx +20 -0
- package/skills/experience-cms-content-render/assets/react/MediaRenderer.tsx +129 -0
- package/skills/experience-cms-content-render/assets/react/TypeList.tsx +40 -0
- package/skills/experience-cms-content-render/assets/react/TypeRenderer.tsx +64 -0
- package/skills/experience-cms-content-render/assets/react/heuristicRenderer.tsx +310 -0
- package/skills/experience-cms-content-render/assets/react/useCmsItem.ts +129 -0
- package/skills/experience-cms-content-render/assets/shared/cmsContentType.ts +49 -0
- package/skills/experience-cms-content-render/assets/shared/cmsCore.types.ts +96 -0
- package/skills/experience-cms-content-render/assets/shared/externalRefs.ts +55 -0
- package/skills/experience-cms-content-render/references/bulk-loading.md +60 -0
- package/skills/experience-cms-content-render/references/codegen-guardrails.md +111 -0
- package/skills/experience-cms-content-render/references/detail-pages.md +87 -0
- package/skills/experience-cms-content-render/references/embed-recipes.md +127 -0
- package/skills/experience-cms-content-render/references/failure-modes.md +96 -0
- package/skills/experience-cms-content-render/references/heuristic-render-rules.md +131 -0
- package/skills/experience-cms-content-render/references/init-scaffold.md +122 -0
- package/skills/experience-cms-content-render/references/interaction-model.md +173 -0
- package/skills/experience-cms-content-render/references/package-api.md +106 -0
- package/skills/experience-cms-content-render/references/schema-sync.md +114 -0
- package/skills/experience-cms-content-render/references/styling-scopes.md +65 -0
- package/skills/experience-cms-content-render/references/verify.md +49 -0
- package/skills/experience-cms-content-type-generate/SKILL.md +2 -2
- package/skills/experience-content-media-stock-image-search/SKILL.md +5 -4
- package/skills/experience-search-coordinate/SKILL.md +198 -0
- package/skills/experience-search-coordinate/assets/search-payload-template.json +25 -0
- package/skills/experience-search-coordinate/references/content-route.md +313 -0
- package/skills/experience-search-coordinate/references/content-type-discovery.md +57 -0
- package/skills/experience-search-coordinate/references/media-route.md +172 -0
- package/skills/experience-search-coordinate/references/scope-resolution.md +14 -0
- package/skills/experience-ui-bundle-localize/SKILL.md +1 -1
- package/skills/experience-ui-bundle-localize/references/i18n-setup.md +5 -3
- package/skills/experience-ui-bundle-project-generate/SKILL.md +18 -14
- package/skills/experience-ui-bundle-project-generate/references/angular-project-generate.md +22 -0
- package/skills/experience-ui-bundle-project-generate/references/react-project-generate.md +20 -0
- package/skills/experience-ui-bundle-salesforce-data-access/SKILL.md +58 -54
- package/skills/experience-ui-bundle-salesforce-data-access/references/caching.md +6 -0
- package/skills/experience-ui-bundle-salesforce-data-access/references/graphiti-cli.md +2 -2
- package/skills/experience-ui-bundle-salesforce-data-access/references/migration.md +6 -0
- package/skills/experience-ui-bundle-salesforce-data-access/references/rest-and-integration.md +2 -1
- package/skills/experience-ui-bundle-salesforce-data-access/references/sdk-api.md +6 -0
- package/skills/experience-ui-bundle-site-generate/SKILL.md +59 -8
- package/skills/experience-ui-bundle-site-generate/references/configure-metadata-digital-experience.md +8 -3
- package/skills/experience-ui-bundle-site-generate/references/configure-metadata-language-settings.md +120 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +336 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +143 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-application-flexipage-mapping.md +127 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-deploy-commands.md +116 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-lifesci-metadata-deploy.md +111 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-overview.md +312 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-profile-layout-assignments.md +171 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-state-tracking.md +64 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-trigger-handlers.md +122 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-4-user-provisioning-overview.md +335 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-4-user-provisioning-user-provisioning-details.md +140 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-execution-state-and-recovery.md +196 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-metadata-cache-generation.md +155 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-overview.md +307 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-visit-creation-data.md +211 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/state-machine-and-changes.md +108 -0
- package/skills/life-sciences-kam-coordinate/SKILL.md +241 -0
- package/skills/life-sciences-kam-coordinate/references/orchestration-flow.md +152 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-application-flexipage-mapping.md +79 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-deploy-commands.md +131 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-kam-config-records.md +85 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-lifesci-metadata-deploy.md +112 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-overview.md +202 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-profile-layout-assignments.md +67 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-state-tracking.md +65 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-trigger-handlers.md +123 -0
- package/skills/life-sciences-kam-coordinate/references/stage-4-participant-role-and-sprint.md +89 -0
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +337 -0
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-creation-data.md +248 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-ipad-validation-script.md +35 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-metadata-cache-generation.md +155 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-user-provisioning-details.md +146 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-user-provisioning-overview.md +89 -0
- package/skills/life-sciences-kam-coordinate/references/state-machine-and-changes.md +114 -0
- package/skills/life-sciences-prerequisites-validate/SKILL.md +138 -0
- package/skills/life-sciences-prerequisites-validate/references/checks-org-settings.md +190 -0
- package/skills/life-sciences-prerequisites-validate/references/checks-user-and-package.md +211 -0
- package/skills/life-sciences-territory-configure/SKILL.md +217 -0
- package/skills/life-sciences-territory-configure/references/territory-metadata.md +262 -0
- package/skills/platform-apex-logs-debug/SKILL.md +7 -7
- package/skills/platform-custom-application-generate/SKILL.md +4 -4
- package/skills/platform-custom-object-generate/SKILL.md +7 -7
- package/skills/platform-custom-tab-generate/SKILL.md +1 -1
- package/skills/platform-dsar-policy-manage/SKILL.md +272 -0
- package/skills/platform-dsar-policy-manage/references/configure.md +106 -0
- package/skills/platform-dsar-policy-manage/references/export-and-history.md +123 -0
- package/skills/platform-dsar-policy-manage/references/gap-analysis-guide.md +150 -0
- package/skills/platform-dsar-policy-manage/references/gap-scan.md +129 -0
- package/skills/platform-dsar-policy-manage/references/headless-sor.md +59 -0
- package/skills/platform-dsar-policy-manage/references/report-format.md +59 -0
- package/skills/platform-dsar-policy-manage/scripts/tests/__init__.py +0 -0
- package/skills/platform-dsar-policy-manage/scripts/tests/test_validate_policy_tree.py +76 -0
- package/skills/platform-dsar-policy-manage/scripts/validate-policy-tree.py +130 -0
- package/skills/platform-flexipage-generate/SKILL.md +4 -0
- package/skills/platform-list-view-generate/SKILL.md +1 -0
- package/skills/platform-salesforce-connect-adapter-generate/SKILL.md +359 -0
- package/skills/platform-salesforce-connect-adapter-generate/references/official-examples.md +69 -0
- package/skills/platform-salesforce-connect-adapter-generate/references/scenarios.md +187 -0
- package/skills/platform-soql-query/SKILL.md +8 -8
- package/skills/platform-value-set-generate/SKILL.md +2 -2
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +20 -27
- package/skills/service-native-voice-recording-transcription-configure/SKILL.md +47 -27
- package/skills/service-native-voice-recording-transcription-configure/references/thunderbird-voice-settings.md +13 -9
- package/skills/service-native-voice-recording-transcription-configure/scripts/enable-recording-transcription.sh +104 -45
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
# AHM Alerts Reference — Agent Health Monitoring Data Alerts
|
|
2
|
+
|
|
3
|
+
End-to-end procedures for creating, listing, deleting, and inspecting **Agent Health Monitoring (AHM)** data alerts, and for verifying the metric values behind them. This is the same functionality the AHM Setup UI performs at `/lightning/n/standard-AgentforceStudio?c__nav=alerts`, but driven entirely from the `sf` CLI.
|
|
4
|
+
|
|
5
|
+
Use this reference when the user wants to:
|
|
6
|
+
|
|
7
|
+
- Create / list / delete an AHM alert on an agent metric (escalation rate, deflection rate, etc.)
|
|
8
|
+
- Understand why an alert isn't firing
|
|
9
|
+
- Inspect triggered alert notifications (trigger history)
|
|
10
|
+
- Discover the alertable SDMs, metrics, and agent filter values on an org
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Contents
|
|
15
|
+
|
|
16
|
+
1. [CLI-first — how commands are expressed here](#cli-first--how-commands-are-expressed-here)
|
|
17
|
+
2. [The endpoint: `tableau/dataAlerts`](#the-endpoint-tableaudataalerts)
|
|
18
|
+
3. [List and describe alerts](#list-and-describe-alerts)
|
|
19
|
+
4. [Prerequisite: confirm the three alertable SDMs](#prerequisite-for-creating-alerts-confirm-the-three-alertable-sdms)
|
|
20
|
+
5. [Enumerate `_mtc` metrics and screen for time-grain fit](#enumerate-_mtc-metrics-and-screen-for-time-grain-fit)
|
|
21
|
+
6. [List the agents available for alert filters](#list-the-agents-available-for-alert-filters)
|
|
22
|
+
7. [Create an alert](#create-an-alert) — POST vs GET field names, `filterContext`, thresholds, full schema, enums, troubleshooting
|
|
23
|
+
8. [Update an alert](#update-an-alert) (not yet available — delete + recreate)
|
|
24
|
+
9. [Delete an alert](#delete-an-alert)
|
|
25
|
+
10. [Trigger history — inspect triggered notifications](#trigger-history--inspect-triggered-notifications)
|
|
26
|
+
11. [Verify metric value via Semantic Engine Gateway](#verify-metric-value-via-semantic-engine-gateway-condensed)
|
|
27
|
+
12. [Global troubleshooting](#global-troubleshooting)
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## CLI-first — how commands are expressed here
|
|
32
|
+
|
|
33
|
+
Every operation is expressed through **`sf api request rest`**, which authenticates and executes with the org credentials the CLI already holds (`-o <org>`).
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Generic shape used throughout this reference
|
|
37
|
+
sf api request rest "<path>" -o <org> # GET
|
|
38
|
+
sf api request rest "<path>" -X POST -H "Content-Type: application/json" -b "@$body" -o <org>
|
|
39
|
+
sf api request rest "<path>/<id>" -X DELETE -o <org>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`sf api request rest` supports `-X/--method` (GET|POST|PUT|PATCH|DELETE|…), `-H/--header`, and `-b/--body` (`@file` to read a file, `-` for stdin, `""` for empty). Pipe the output to `python3 -m json.tool` to pretty-print.
|
|
43
|
+
|
|
44
|
+
> **Request bodies go in a private temp file — never a fixed `/tmp/<name>.json`.** The POST bodies below carry org data (SDM/agent names, user IDs, alert config). A predictable, world-readable path in the shared `/tmp` namespace invites info disclosure, symlink clobbering, and a TOCTOU swap between the `cat >` write and the `-b @file` read. Create a per-invocation `0600` file, pass it, and remove it on exit:
|
|
45
|
+
>
|
|
46
|
+
> ```bash
|
|
47
|
+
> body=$(mktemp) && chmod 600 "$body"
|
|
48
|
+
> trap 'rm -f "$body"' EXIT
|
|
49
|
+
> # cat > "$body" <<'JSON' … then: -b "@$body"
|
|
50
|
+
> ```
|
|
51
|
+
>
|
|
52
|
+
> Every heredoc example below assumes this `body=$(mktemp)` idiom (reuse the same `trap` for the run) and writes to `"$body"` rather than a literal `/tmp/*.json`.
|
|
53
|
+
|
|
54
|
+
> **Notes.**
|
|
55
|
+
> - **The org must already be authenticated — these commands do not launch a login.** Every call uses the credentials the CLI already holds for `-o <org>`. Pre-flight with `sf org display --target-org <org>` (or `sf org list`); if the alias is missing/unauthenticated, `sf` fails with `No authorization information found for <alias>` rather than prompting. Authenticate with `sf org login web --alias <alias>` — a browser flow the **user** runs (an agent can't complete it; in Claude Code prefix the command with `!`). An **expired** token auto-refreshes on the next call; a **missing/removed** auth does not.
|
|
56
|
+
> - There is **no dedicated `sf agent alert …` subcommand today** — the generic `sf api request rest` wrapper is the CLI surface for AHM alerts. If/when dedicated subcommands ship, prefer them and their `--help`.
|
|
57
|
+
> - `sf api request rest` is GA and bundled with the Salesforce CLI (no extra plugin required); it is the sole API surface used throughout this reference.
|
|
58
|
+
> - **API version.** Every endpoint below uses **`/services/data/v66.0/`**. For these resources the data API version is largely orthogonal to resource availability — v66.0 is the standardized floor here and `v68.0` also works on current orgs. Only raise the version if you need a field added in a later release; if any call unexpectedly returns `404`, retry at the org's current API version before assuming the resource is missing.
|
|
59
|
+
> - Reuse **Phase 0 (Discover Data Space)** from the main skill for the `dataspace` value used by the Data Cloud SQL and semantic queries below (default: `default`).
|
|
60
|
+
|
|
61
|
+
### Resolve the owner user ID
|
|
62
|
+
|
|
63
|
+
The list endpoint requires an `ownerId`. Resolve the current user (or another user by username):
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Current user (preferred — no user-supplied input)
|
|
67
|
+
USER_ID=$(sf org display user --target-org <org> --json \
|
|
68
|
+
| python3 -c 'import sys,json;print(json.load(sys.stdin)["result"]["id"])')
|
|
69
|
+
|
|
70
|
+
# Another user, supplied out-of-band as a validated Salesforce User Id (15 or 18 chars).
|
|
71
|
+
# Prefer an Id over a username — it needs no query and can't inject.
|
|
72
|
+
USER_ID="005XXXXXXXXXXXXXXX"
|
|
73
|
+
[[ "$USER_ID" =~ ^[a-zA-Z0-9]{15,18}$ ]] || { echo "Not a valid User Id" >&2; exit 1; }
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Do not** build the owner query by interpolating a free-text username into `--query "… Username = '<value>'"`. That value crosses two hostile boundaries at once — the SOQL string literal (`x' OR IsActive=true LIMIT 1` subverts the predicate → resolves an arbitrary user's Id) and the surrounding double-quoted shell word (`$(…)`/backticks execute before `sf` runs). `agentforce-observe` reads production session data, so a username can be attacker-influenced, not just operator-typed.
|
|
77
|
+
|
|
78
|
+
If a username lookup is genuinely unavoidable, first reject anything with quoting/shell metacharacters, then build the SOQL in a `python3` snippet that reads the value from an environment variable and escapes the literal — never inline in a double-quoted `--query`:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
UNAME='other@example.com'
|
|
82
|
+
# Reject any quote / backtick / $ / backslash / ; before the value is used.
|
|
83
|
+
printf '%s' "$UNAME" | LC_ALL=C grep -q "[\"'\`\$\\;]" && { echo "Reject: unsafe chars in username" >&2; exit 1; }
|
|
84
|
+
# UNAME is already validated above (no quotes/backslashes/shell metachars), so interpolate it directly.
|
|
85
|
+
SOQL=$(UNAME="$UNAME" python3 -c \
|
|
86
|
+
'import os;print("SELECT Id FROM User WHERE Username = \x27%s\x27" % os.environ["UNAME"])')
|
|
87
|
+
USER_ID=$(sf data query --target-org <org> --json --query "$SOQL" \
|
|
88
|
+
| python3 -c 'import sys,json;print(json.load(sys.stdin)["result"]["records"][0]["Id"])')
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## The endpoint: `tableau/dataAlerts`
|
|
94
|
+
|
|
95
|
+
AHM alerts are a special flavor of Salesforce data alerts — `dataAlertType: "agenthealthmonitoring"` — exposed under a dedicated REST resource:
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
/services/data/v66.0/tableau/dataAlerts
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
They are **not** served from `/wave/dataAlerts` (CRM Analytics) or `/analytics/dataAlerts`. The "tableau" namespace here is Tableau Next / Data Cloud analytics, distinct from classic Wave — `/tableau/dataAlerts` does **not** require Wave / CRM Analytics.
|
|
102
|
+
|
|
103
|
+
| Method | Path | Purpose |
|
|
104
|
+
|--------|------|---------|
|
|
105
|
+
| GET | `/services/data/v66.0/tableau/dataAlerts?ownerId={userId}` | List alerts for a user (requires `ownerId`) |
|
|
106
|
+
| POST | `/services/data/v66.0/tableau/dataAlerts` | Create |
|
|
107
|
+
| PUT | `/services/data/v66.0/tableau/dataAlerts/{alertId}` | Update (not yet available — see below) |
|
|
108
|
+
| DELETE | `/services/data/v66.0/tableau/dataAlerts/{alertId}` | Delete |
|
|
109
|
+
| GET | `/services/data/v66.0/tableau/dataAlerts/{alertId}` | Not supported — 405 Method Not Allowed |
|
|
110
|
+
|
|
111
|
+
Single-alert GET is not supported — list all and filter client-side. Omitting `ownerId` on the list GET returns `400 MISSING_PARAM: Owner ID cannot be empty`. There is no "list all alerts across the org" shape; always scope to a user.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## List and describe alerts
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
sf api request rest "/services/data/v66.0/tableau/dataAlerts?ownerId=$USER_ID" -o <org> \
|
|
119
|
+
| python3 -m json.tool
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Example response (`totalSize: 1`):
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"dataAlerts": [
|
|
127
|
+
{
|
|
128
|
+
"id": "3VRxx0000001xxxxAY",
|
|
129
|
+
"alertName": "te1_AHM_High_AHM_Escalation_Rate_mtc::Service_Agent_Analytics_SDM_1f8_AHM_Above_0.1_AHM_All",
|
|
130
|
+
"dataAlertType": "agenthealthmonitoring",
|
|
131
|
+
"createdDate": "2026-05-01T23:01:02.000Z",
|
|
132
|
+
"schedule": {"type": "everynminutes", "minuteLevelFrequency": 1},
|
|
133
|
+
"thresholds": {
|
|
134
|
+
"conditions": [{
|
|
135
|
+
"leftOperand": {
|
|
136
|
+
"type": "insights",
|
|
137
|
+
"factKey": "FACT_KEY_TARGET_PERIOD_VALUE",
|
|
138
|
+
"filterContext": [],
|
|
139
|
+
"insightType": "popc",
|
|
140
|
+
"metricId": "1HUxx000000xxxx4AE",
|
|
141
|
+
"modelApiNameOrId": "2SMxx000000xxxx4AY",
|
|
142
|
+
"timeContext": {"operator": "LastNHours", "values": ["1"]}
|
|
143
|
+
},
|
|
144
|
+
"operator": "greaterorequal",
|
|
145
|
+
"rightOperand": {"type": "rawvalue", "dataType": "number", "value": "0.1"}
|
|
146
|
+
}],
|
|
147
|
+
"customLogicalOperation": "1"
|
|
148
|
+
},
|
|
149
|
+
"deliveryConfigurations": {
|
|
150
|
+
"receivers": [
|
|
151
|
+
{"type": "notification", "recipients": ["005xx000001xxxxAAC"]},
|
|
152
|
+
{"type": "email", "recipients": ["005xx000001xxxxAAC"]}
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
],
|
|
157
|
+
"totalSize": 1
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Observations from a live response:
|
|
162
|
+
|
|
163
|
+
1. **`alertName` is an encoded form** — `_AHM_`-separated: `<freeText>_AHM_<severity>_AHM_<metricApiName>::<sdmApiName>_AHM_<operator>_<threshold>_AHM_<scope>`.
|
|
164
|
+
2. **`modelApiNameOrId` can be an ID** (e.g. `2SMxx…`), not always an API name.
|
|
165
|
+
3. **`filterContext: []`** even when the alert name indicates filters — this is a **known bug**. The real filters live in the **sub-metric** referenced by `metricId`.
|
|
166
|
+
4. **`schedule.minuteLevelFrequency: 1`** — fires every minute (aggressive; useful for test/demo).
|
|
167
|
+
|
|
168
|
+
### Retrieve the sub-metric to see the actual filters
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
METRIC_ID="1HUxx000000xxxx4AE"
|
|
172
|
+
MODEL="Service_Agent_Analytics_SDM" # or the ID from modelApiNameOrId
|
|
173
|
+
sf api request rest \
|
|
174
|
+
"/services/data/v66.0/ssot/semantic/models/${MODEL}/sub-metrics/${METRIC_ID}" -o <org> \
|
|
175
|
+
| python3 -m json.tool
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
HTTP 200 → it's a filtered sub-metric; `filters[]` holds the real agent-name / agent-type conditions.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Prerequisite for creating alerts: confirm the three alertable SDMs
|
|
183
|
+
|
|
184
|
+
> **Scope rule.** AHM data alerts target **exactly three** semantic data models. **Ignore every other SDM** returned by the endpoint — including `Agent_Health_Monitoring_SDM`, dataset-specific models, or custom models. If the metric you want isn't in one of the three below, the answer is "not supported," not "try another SDM."
|
|
185
|
+
|
|
186
|
+
1. **Agentforce Analytics Foundations** — base model. API name `sfm_Agentforce_Analytics_Foundations` (stable). Exposes only `_clc`, no `_mtc`.
|
|
187
|
+
2. **Employee Agent Analytics SDM** — match by **label** `Employee Agent Analytics SDM` or **`app` = `Employee_Agent_Analytics`**.
|
|
188
|
+
3. **Service Agent Analytics SDM** — match by **label** `Service Agent Analytics SDM` or **`app` = `Service_Agent_Analytics`**.
|
|
189
|
+
|
|
190
|
+
> **Never hard-code the `_1f8` (or any other) `apiName` suffix.** It's a content-hash / provisioning token that changes when the app template is re-provisioned or moved to another org. Always discover the live `apiName` by listing models and matching on the stable fields (`label`, `app`), then read `apiName` back out.
|
|
191
|
+
|
|
192
|
+
### List all semantic models
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
sf api request rest "/services/data/v66.0/ssot/semantic/models" -o <org> \
|
|
196
|
+
| python3 -m json.tool
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Example `items[]` entry:
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"apiName": "Service_Agent_Analytics_SDM_1f8",
|
|
204
|
+
"app": "Service_Agent_Analytics",
|
|
205
|
+
"label": "Service Agent Analytics SDM",
|
|
206
|
+
"id": "2SMxx000000xxxx4AY",
|
|
207
|
+
"dataspace": "default",
|
|
208
|
+
"baseModels": [
|
|
209
|
+
{"apiName": "sfm_Agentforce_Analytics_Foundations", "label": "Agentforce Analytics Foundations"}
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Key fields: `label` (stable match), `apiName` (goes into `modelApiNameOrId`, has the suffix), `app`/`sourceCreationName` (stable app identifiers), `id` (SDM record ID — needed for the semantic-engine gateway).
|
|
215
|
+
|
|
216
|
+
### Provisioning check — the three only, ignore everything else
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
export ORG=<org>
|
|
220
|
+
sf api request rest "/services/data/v66.0/ssot/semantic/models" -o "$ORG" | python3 <<'PY'
|
|
221
|
+
import json, sys
|
|
222
|
+
models = json.load(sys.stdin).get("items", [])
|
|
223
|
+
|
|
224
|
+
# Allowlist: exactly the three AHM-alertable SDMs. Match by stable fields only.
|
|
225
|
+
IN_SCOPE = [
|
|
226
|
+
("Agentforce Analytics Foundations", None),
|
|
227
|
+
("Employee Agent Analytics SDM", "Employee_Agent_Analytics"),
|
|
228
|
+
("Service Agent Analytics SDM", "Service_Agent_Analytics"),
|
|
229
|
+
]
|
|
230
|
+
|
|
231
|
+
def match(m, label, app):
|
|
232
|
+
return m.get("label") == label or (app and m.get("app") == app)
|
|
233
|
+
|
|
234
|
+
print(f"{'SDM (in scope)':40} {'apiName':45} {'id':25} status")
|
|
235
|
+
print("-" * 120)
|
|
236
|
+
resolved_ids = set()
|
|
237
|
+
for label, app in IN_SCOPE:
|
|
238
|
+
hits = [m for m in models if match(m, label, app)]
|
|
239
|
+
if not hits:
|
|
240
|
+
print(f"{label:40} {'(not found)':45} {'':25} Not provisioned — cannot alert on this SDM")
|
|
241
|
+
continue
|
|
242
|
+
m = hits[0]
|
|
243
|
+
resolved_ids.add(m.get("id"))
|
|
244
|
+
print(f"{label:40} {m.get('apiName','?'):45} {str(m.get('id','?')):25} provisioned")
|
|
245
|
+
|
|
246
|
+
ignored = [m for m in models if m.get("id") not in resolved_ids]
|
|
247
|
+
print(f"\nIgnored — out of scope ({len(ignored)} model(s)). Do NOT alert on these:")
|
|
248
|
+
for m in ignored:
|
|
249
|
+
print(f" • {m.get('label','?')} (apiName={m.get('apiName','?')})")
|
|
250
|
+
if not ignored:
|
|
251
|
+
print(" (none)")
|
|
252
|
+
PY
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Interpreting the output:
|
|
256
|
+
- All three provisioned → proceed to create alerts; use the `apiName` values as-is for `modelApiNameOrId`.
|
|
257
|
+
- Any Not provisioned → that SDM's app template is not provisioned on this org. Alerts cannot be created against it until the template is deployed (a provisioning task, not a CLI workaround).
|
|
258
|
+
- Anything "Ignored" → acknowledge and move on. **Do not** substitute an ignored SDM for a missing in-scope one.
|
|
259
|
+
|
|
260
|
+
Troubleshooting:
|
|
261
|
+
- **Only Foundations shows up** — the Employee/Service app templates haven't been provisioned; they must be deployed via the Analytics app template flow first.
|
|
262
|
+
- **`FUNCTIONALITY_NOT_ENABLED` on `/ssot/semantic/models`** — Data Cloud / SSOT is not provisioned; none of the alertable SDMs can exist.
|
|
263
|
+
- **API name doesn't match `modelApiNameOrId` in an existing alert** — the alert was created against an older provisioning hash. Delete and recreate against the current `apiName`.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Enumerate `_mtc` metrics and screen for time-grain fit
|
|
268
|
+
|
|
269
|
+
The alertable atoms inside each SDM are **semantic metrics** whose API names end in `_mtc`. These differ from the `_clc` calculated measurements:
|
|
270
|
+
|
|
271
|
+
| Endpoint | Object | Suffix | What it is |
|
|
272
|
+
|---|---|---|---|
|
|
273
|
+
| `…/calculated-measurements` | Calculated measurement | `_clc` | Low-level aggregation at the SDM layer (raw ingredient). |
|
|
274
|
+
| `…/metrics` | Semantic metric | `_mtc` | Wraps a `_clc` with time dimension, grains, insight settings. **This is what data alerts reference.** |
|
|
275
|
+
| `…/sub-metrics` | Sub-metric | id `1HU…` | A filtered instantiation of a `_mtc`. Most real alerts' `metricId` is a sub-metric, not the bare `_mtc`. |
|
|
276
|
+
|
|
277
|
+
### List all `_mtc` across the three SDMs
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
export ORG=<org>
|
|
281
|
+
python3 <<'PY'
|
|
282
|
+
import json, subprocess
|
|
283
|
+
|
|
284
|
+
def rest(path):
|
|
285
|
+
out = subprocess.check_output(["sf","api","request","rest",path,"-o",__import__("os").environ["ORG"]])
|
|
286
|
+
return json.loads(out)
|
|
287
|
+
|
|
288
|
+
models = rest("/services/data/v66.0/ssot/semantic/models").get("items", [])
|
|
289
|
+
IN_SCOPE_APPS = {
|
|
290
|
+
"Agentforce Analytics Foundations": None,
|
|
291
|
+
"Employee Agent Analytics SDM": "Employee_Agent_Analytics",
|
|
292
|
+
"Service Agent Analytics SDM": "Service_Agent_Analytics",
|
|
293
|
+
}
|
|
294
|
+
resolved = {}
|
|
295
|
+
for m in models:
|
|
296
|
+
for label, app in IN_SCOPE_APPS.items():
|
|
297
|
+
if m.get("label") == label or (app and m.get("app") == app):
|
|
298
|
+
resolved[label] = m["apiName"]
|
|
299
|
+
break
|
|
300
|
+
|
|
301
|
+
for label, api in resolved.items():
|
|
302
|
+
metrics = rest(f"/services/data/v66.0/ssot/semantic/models/{api}/metrics").get("metrics", [])
|
|
303
|
+
mtc = [m for m in metrics if m.get("apiName","").endswith("_mtc")]
|
|
304
|
+
print(f"\n=== {label} ({api}) — {len(mtc)} _mtc metric(s) ===")
|
|
305
|
+
for m in mtc:
|
|
306
|
+
grains = ",".join(m.get("timeGrains", [])) or "(no timeGrains)"
|
|
307
|
+
print(f" {m['apiName']:40} | {m.get('label',''):30} | id={m.get('id','')} | grains=[{grains}]")
|
|
308
|
+
if not mtc:
|
|
309
|
+
print(" (none — e.g. Foundations exposes only _clc)")
|
|
310
|
+
PY
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
On a typical org, Foundations exposes **0** `_mtc`; Service Analytics SDM exposes ~14 (`Escalation_Rate_mtc`, `Deflection_Rate_mtc`, `Abandonment_Rate_mtc`, `Engagement_Rate_mtc`, `Agent_Health_Score_mtc`, `Total_Sessions_mtc`, …); Employee Analytics SDM exposes ~9 (`Stickiness_Rate_mtc`, `Total_Unique_Users_mtc`, `Weekly_Active_Users_mtc`, …). All declare `timeGrains = [Day, Week, Month, Quarter, Year]` — none declare Hour/Minute, but the `dataAlerts` API doesn't enforce this (the alert's `timeContext.operator` is independent).
|
|
314
|
+
|
|
315
|
+
Pull the `id` of the chosen `_mtc` — it goes into `thresholds.conditions[].leftOperand.metricId` when creating an alert.
|
|
316
|
+
|
|
317
|
+
### Screening: does the metric make sense at a short window?
|
|
318
|
+
|
|
319
|
+
The SDM doesn't block nonsense; apply judgement:
|
|
320
|
+
|
|
321
|
+
| Metric shape | Smallest useful window | Short-window (≤ 1h) alert? |
|
|
322
|
+
|---|---|---|
|
|
323
|
+
| **Ratios / rates** — Engagement, Deflection, Abandonment, Escalation, Stickiness | 15 min – 1 h | Yes — self-normalizing. Add a minimum-volume guard. |
|
|
324
|
+
| **Averages / times** — Avg Time to Deflection/Escalation/Execution, Avg Quality Score | ≥ 1 h | Borderline at 1–4h; daily is fine. Not at 15 min. |
|
|
325
|
+
| **Cumulative totals** — Total/Deflected/Escalated/Abandoned/Engaged Sessions, Total Actions | ≥ 1 h, typically ≥ 1 day | No — noisy at short windows — alert on the corresponding **rate** instead. |
|
|
326
|
+
| **Cardinality counts** — Unique Users, Weekly Active Users, Total Employee Agents | ≥ 1 day | No — smallest sensible window Day (DAU) / Week (WAU). |
|
|
327
|
+
| **Composite scores** — Agent Health Score | default ≥ 1 day | Depends on inputs; default no. |
|
|
328
|
+
|
|
329
|
+
> **Minimum-volume guard.** Even for ratios, check the denominator: a 100% error rate on 1 session in 15 min is noise. The API has no built-in guard — pre-filter via a sub-metric requiring N > threshold sessions, or combine a rate threshold AND an absolute-count threshold in `thresholds.conditions[]`.
|
|
330
|
+
|
|
331
|
+
Use short-window rate metrics with `schedule.minuteLevelFrequency ≤ 60` and `timeContext.operator = LastNHours|LastNMinutes`; use daily+ metrics only with `minuteLevelFrequency ≥ 1440` and `LastNDays`/`LastNWeeks`.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## List the agents available for alert filters
|
|
336
|
+
|
|
337
|
+
The UI Create-Alert modal scopes an alert to an **Agent Name** or **Agent Type**. Those values come from **live session-tracing data (Data Cloud DLO)**, not from agent metadata.
|
|
338
|
+
|
|
339
|
+
> **For AHM alerts, use the DLO query (ground truth) below.** If a name appears in `BotDefinition` but has never run a session, the filter selects zero sessions and the alert never fires. A name in the DLO without a `BotDefinition` (demo/external agents) is still alertable — the filter matches strings, not IDs.
|
|
340
|
+
>
|
|
341
|
+
> **Cross-link:** the DLO object below, `ssot__AiAgentSessionParticipant__dlm`, is the same DMO documented in [`stdm-schema.md`](stdm-schema.md). Note the AHM filter uses `ssot__AiAgentApiName__c` here, which is **not** the same as the `GenAiPlannerDefinition` `MasterLabel`/`DeveloperName` the main skill resolves for STDM `findSessions` — grab the filter value from this query, don't reconstruct it.
|
|
342
|
+
|
|
343
|
+
### DLO query (ground truth)
|
|
344
|
+
|
|
345
|
+
Write the SQL body to a private temp file (see the `mktemp` idiom above) to avoid shell-escaping, then POST it:
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
body=$(mktemp) && chmod 600 "$body"
|
|
349
|
+
trap 'rm -f "$body"' EXIT
|
|
350
|
+
cat > "$body" <<'JSON'
|
|
351
|
+
{
|
|
352
|
+
"sql": "SELECT ssot__AiAgentApiName__c AS agent_name, ssot__AiAgentType__c AS agent_type, COUNT(*) AS participant_rows FROM ssot__AiAgentSessionParticipant__dlm WHERE ssot__AiAgentSessionParticipantRole__c != 'USER' GROUP BY ssot__AiAgentApiName__c, ssot__AiAgentType__c ORDER BY agent_name",
|
|
353
|
+
"rowLimit": 500,
|
|
354
|
+
"adaptiveTimeout": 1
|
|
355
|
+
}
|
|
356
|
+
JSON
|
|
357
|
+
|
|
358
|
+
sf api request rest "/services/data/v66.0/ssot/query-sql?dataspace=default" \
|
|
359
|
+
-X POST -H "Content-Type: application/json" -b "@$body" -o <org> \
|
|
360
|
+
| python3 -m json.tool
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
The `ssot__AiAgentSessionParticipantRole__c != 'USER'` clause filters out the human side of the conversation. `GROUP BY` already yields one row per distinct `(agent_name, agent_type)` pair — no `DISTINCT` needed. In the `data` array: column 0 = `agent_name` (→ `AI_Agent_Api_Name` filter values), column 1 = `agent_type`, column 2 = `participant_rows` (volume sanity check — an agent with a handful of rows is too sparse for a 15-min alert).
|
|
364
|
+
|
|
365
|
+
### Metadata cross-reference (fallback)
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
sf data query --target-org <org> \
|
|
369
|
+
--query "SELECT Id, DeveloperName, MasterLabel, AgentType FROM BotDefinition ORDER BY DeveloperName"
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Not required for alerting. `AiAgent` sObject is often not queryable via REST — don't rely on it.
|
|
373
|
+
|
|
374
|
+
### Next-Gen Authoring Bundles API (what agents are defined)
|
|
375
|
+
|
|
376
|
+
Works even where `BotDefinition`/`AiAgent` sObjects are not queryable (common on demo orgs):
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
sf api request rest "/services/data/v66.0/nextgen-authoring/bundles" -o <org> | python3 -m json.tool
|
|
380
|
+
# names only:
|
|
381
|
+
sf api request rest "/services/data/v66.0/nextgen-authoring/bundles" -o <org> \
|
|
382
|
+
| python3 -c 'import sys,json; [print(b.get("apiName","?")) for b in json.load(sys.stdin).get("bundles", [])]'
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
`apiName` is the agent's developer name (the value usable as an alert filter); `isLegacy: false` = new Next-Gen agent. Requires `NextGenAuthoring.orgHasNextGenAgentAuthoringEnabled` + `…userCanAccessNextGenAgentAuthoring`, else 403 (the `.get("bundles", [])` above then prints nothing rather than raising).
|
|
386
|
+
|
|
387
|
+
**Use the DLO query (ground truth) for filter values.** The metadata cross-reference and Bundles API are for discovery/cross-ref only. An agent listed there but absent from the DLO has never run a session — alerting on it matches zero rows until sessions arrive.
|
|
388
|
+
|
|
389
|
+
Troubleshooting:
|
|
390
|
+
- **`query-sql` 400 / FUNCTIONALITY_NOT_ENABLED** — SSOT/Data Cloud not enabled; no agent-tracing data exists and alerts can't fire.
|
|
391
|
+
- **Zero rows** — no sessions have landed. Confirm session-tracing is on and data has flowed.
|
|
392
|
+
- **UI dropdown shows values not in the DLO query** — check the `dataspace`; change `?dataspace=` if the alert is scoped to a non-`default` space.
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## Create an alert
|
|
397
|
+
|
|
398
|
+
`POST /services/data/v66.0/tableau/dataAlerts`. Inputs come from the sections above: `modelApiNameOrId` (SDM `apiName`/`id`), `metricId` (`_mtc` `id`), and `filterContext` (agent name/type from the DLO query above).
|
|
399
|
+
|
|
400
|
+
### POST field names ≠ GET field names
|
|
401
|
+
|
|
402
|
+
The POST input representation uses **different field names and casing** from the GET response:
|
|
403
|
+
|
|
404
|
+
| POST (input) | GET (output) | Notes |
|
|
405
|
+
|---|---|---|
|
|
406
|
+
| `utterance` | `alertName` | POST field is `utterance`; response returns `alertName` |
|
|
407
|
+
| `type: "Metric"` (leftOperand) | `type: "insights"` | PascalCase in, lowercase out |
|
|
408
|
+
| `type: "RawValue"` (rightOperand) | `type: "rawvalue"` | same pattern |
|
|
409
|
+
| `type: "EveryNMinutes"` (schedule) | `type: "everynminutes"` | same |
|
|
410
|
+
| `type: "Notification"` / `"Email"` | `type: "notification"` / `"email"` | same |
|
|
411
|
+
| `operator: "GreaterOrEqual"` | `operator: "greaterorequal"` | same |
|
|
412
|
+
| `insightType: "Popc"` | `insightType: "popc"` | same |
|
|
413
|
+
|
|
414
|
+
**Rule of thumb:** all `type` discriminators and enum values in the POST body use **PascalCase**; the GET response lowercases everything. Copying a GET response and POSTing it back fails with `JSON_PARSER_ERROR`.
|
|
415
|
+
|
|
416
|
+
### filterContext field-name format
|
|
417
|
+
|
|
418
|
+
Filter field names use **dot notation**: `{tableApiName}.{fieldApiName}`. The metric's `additionalDimensions` lists the available dimensions as `tableFieldReference` objects — concatenate `tableApiName` + `.` + `fieldApiName`:
|
|
419
|
+
|
|
420
|
+
```text
|
|
421
|
+
tableApiName: "Agent_API_Name_lv"
|
|
422
|
+
fieldApiName: "AI_Agent_Session_Participant2_AI_Agent_Api_Name"
|
|
423
|
+
→ filterContext fieldName: "Agent_API_Name_lv.AI_Agent_Session_Participant2_AI_Agent_Api_Name"
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Using just the `fieldApiName` fails with `INTERNAL_ERROR: Validation Failed: Invalid calculated Field`.
|
|
427
|
+
|
|
428
|
+
### Sub-metric auto-creation
|
|
429
|
+
|
|
430
|
+
When you POST with a non-empty `filterContext`, the backend **auto-creates a sub-metric** (ID prefix `1HU…`) from the bare `_mtc` id plus your filters. The response's `metricId` is the new sub-metric id, not the `_mtc` id you sent. You don't create sub-metrics manually. (The response's `filterContext` comes back `[]` — the known bug; fetch the sub-metric to see the real filters.)
|
|
431
|
+
|
|
432
|
+
### Threshold values are raw ratios, not display percentages
|
|
433
|
+
|
|
434
|
+
Rate metrics are a **0–1 ratio** (the UI multiplies by 100 for display; the alert threshold operates on the raw value):
|
|
435
|
+
|
|
436
|
+
| You want to alert at | Threshold `value` |
|
|
437
|
+
|---|---|
|
|
438
|
+
| 1% | `"0.01"` |
|
|
439
|
+
| 5% | `"0.05"` |
|
|
440
|
+
| 50% | `"0.5"` |
|
|
441
|
+
| 100% | `"1"` |
|
|
442
|
+
|
|
443
|
+
> `value: "1"` means ≥ 100%, not ≥ 1%. An alert with `"1"` on a rate metric is either a test alert designed to always fire, or a misconfiguration.
|
|
444
|
+
|
|
445
|
+
### Full POST schema
|
|
446
|
+
|
|
447
|
+
```json
|
|
448
|
+
{
|
|
449
|
+
"utterance": "<alert name — encoded or freeform string>",
|
|
450
|
+
"dataAlertType": "agenthealthmonitoring",
|
|
451
|
+
"schedule": { "type": "EveryNMinutes", "minuteLevelFrequency": <integer minutes> },
|
|
452
|
+
"content": { "type": "Metric", "modelApiNameOrId": ["<SDM ID or apiName>"] },
|
|
453
|
+
"thresholds": {
|
|
454
|
+
"conditions": [
|
|
455
|
+
{
|
|
456
|
+
"leftOperand": {
|
|
457
|
+
"type": "Metric",
|
|
458
|
+
"modelApiNameOrId": "<SDM ID or apiName>",
|
|
459
|
+
"metricId": "<_mtc id>",
|
|
460
|
+
"insightType": "Popc",
|
|
461
|
+
"factKey": "FACT_KEY_TARGET_PERIOD_VALUE",
|
|
462
|
+
"params": {},
|
|
463
|
+
"filterContext": [
|
|
464
|
+
{ "fieldName": "<table.field dot notation>", "operator": "Equals", "values": ["<filter value>"] }
|
|
465
|
+
],
|
|
466
|
+
"timeContext": { "operator": "LastNHours", "values": ["<hours>"] }
|
|
467
|
+
},
|
|
468
|
+
"operator": "GreaterOrEqual",
|
|
469
|
+
"rightOperand": { "type": "RawValue", "dataType": "Number", "value": "<threshold as string>" }
|
|
470
|
+
}
|
|
471
|
+
],
|
|
472
|
+
"customLogicalOperation": "1"
|
|
473
|
+
},
|
|
474
|
+
"deliveryConfigurations": {
|
|
475
|
+
"receivers": [
|
|
476
|
+
{ "type": "Notification", "recipients": ["<userId>"] },
|
|
477
|
+
{ "type": "Email", "recipients": ["<userId>"] }
|
|
478
|
+
]
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Example: Escalation Rate ≥ 1% for agent `te1`, checked every 15 minutes
|
|
484
|
+
|
|
485
|
+
Build the body in a private temp file (see the `mktemp` idiom above), then POST it. The literal-value heredoc below shows the exact shape (verified against a real org) with **constant** example values — safe to paste as-is because nothing is substituted:
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
body=$(mktemp) && chmod 600 "$body"
|
|
489
|
+
trap 'rm -f "$body"' EXIT
|
|
490
|
+
cat > "$body" <<'JSON'
|
|
491
|
+
{
|
|
492
|
+
"utterance": "te1_AHM_High_AHM_Escalation_Rate_mtc::Service_Agent_Analytics_SDM_1f8_AHM_Above_0.01_AHM_te1",
|
|
493
|
+
"dataAlertType": "agenthealthmonitoring",
|
|
494
|
+
"schedule": { "type": "EveryNMinutes", "minuteLevelFrequency": 15 },
|
|
495
|
+
"content": { "type": "Metric", "modelApiNameOrId": ["2SMSG000000a6TV4AY"] },
|
|
496
|
+
"thresholds": {
|
|
497
|
+
"conditions": [
|
|
498
|
+
{
|
|
499
|
+
"leftOperand": {
|
|
500
|
+
"type": "Metric",
|
|
501
|
+
"modelApiNameOrId": "2SMSG000000a6TV4AY",
|
|
502
|
+
"metricId": "1DOSG0000051KAm4AM",
|
|
503
|
+
"insightType": "Popc",
|
|
504
|
+
"factKey": "FACT_KEY_TARGET_PERIOD_VALUE",
|
|
505
|
+
"params": {},
|
|
506
|
+
"filterContext": [
|
|
507
|
+
{ "fieldName": "Agent_API_Name_lv.AI_Agent_Session_Participant2_AI_Agent_Api_Name", "operator": "Equals", "values": ["te1"] }
|
|
508
|
+
],
|
|
509
|
+
"timeContext": { "operator": "LastNHours", "values": ["1"] }
|
|
510
|
+
},
|
|
511
|
+
"operator": "GreaterOrEqual",
|
|
512
|
+
"rightOperand": { "type": "RawValue", "dataType": "Number", "value": "0.01" }
|
|
513
|
+
}
|
|
514
|
+
],
|
|
515
|
+
"customLogicalOperation": "1"
|
|
516
|
+
},
|
|
517
|
+
"deliveryConfigurations": {
|
|
518
|
+
"receivers": [
|
|
519
|
+
{ "type": "Notification", "recipients": ["<USER_ID>"] },
|
|
520
|
+
{ "type": "Email", "recipients": ["<USER_ID>"] }
|
|
521
|
+
]
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
JSON
|
|
525
|
+
|
|
526
|
+
sf api request rest "/services/data/v66.0/tableau/dataAlerts" \
|
|
527
|
+
-X POST -H "Content-Type: application/json" -b "@$body" -o <org> \
|
|
528
|
+
| python3 -m json.tool
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
**When the agent name, `utterance`, filter value, threshold, or recipient come from data — not typed by hand — do not string-substitute them into the heredoc.** An agent name sourced from live DLO/session data can contain a `"` that breaks out of the JSON string and injects sibling fields (e.g. an attacker-controlled `Email` receiver that exfiltrates notifications, or a corrupted threshold that silently disables the alert). Reject values containing `"`/`\`, then let a JSON-aware builder encode everything — the body is byte-safe regardless of what the values contain:
|
|
532
|
+
|
|
533
|
+
```bash
|
|
534
|
+
# AGENT_NAME / UTTERANCE / THRESHOLD (raw ratio) / MODEL_ID / METRIC_ID / USER_ID come from earlier discovery.
|
|
535
|
+
case "$AGENT_NAME$UTTERANCE" in *[\"\\]*) echo "Reject: quote/backslash in name or utterance" >&2; exit 1;; esac
|
|
536
|
+
body=$(mktemp) && chmod 600 "$body"
|
|
537
|
+
trap 'rm -f "$body"' EXIT
|
|
538
|
+
AGENT_NAME="$AGENT_NAME" UTTERANCE="$UTTERANCE" THRESHOLD="$THRESHOLD" \
|
|
539
|
+
MODEL_ID="$MODEL_ID" METRIC_ID="$METRIC_ID" USER_ID="$USER_ID" python3 - > "$body" <<'PY'
|
|
540
|
+
import os, sys, json
|
|
541
|
+
e = os.environ
|
|
542
|
+
json.dump({
|
|
543
|
+
"utterance": e["UTTERANCE"],
|
|
544
|
+
"dataAlertType": "agenthealthmonitoring",
|
|
545
|
+
"schedule": {"type": "EveryNMinutes", "minuteLevelFrequency": 15},
|
|
546
|
+
"content": {"type": "Metric", "modelApiNameOrId": [e["MODEL_ID"]]},
|
|
547
|
+
"thresholds": {"conditions": [{
|
|
548
|
+
"leftOperand": {"type": "Metric", "modelApiNameOrId": e["MODEL_ID"], "metricId": e["METRIC_ID"],
|
|
549
|
+
"insightType": "Popc", "factKey": "FACT_KEY_TARGET_PERIOD_VALUE", "params": {},
|
|
550
|
+
"filterContext": [{"fieldName": "Agent_API_Name_lv.AI_Agent_Session_Participant2_AI_Agent_Api_Name",
|
|
551
|
+
"operator": "Equals", "values": [e["AGENT_NAME"]]}],
|
|
552
|
+
"timeContext": {"operator": "LastNHours", "values": ["1"]}},
|
|
553
|
+
"operator": "GreaterOrEqual",
|
|
554
|
+
"rightOperand": {"type": "RawValue", "dataType": "Number", "value": e["THRESHOLD"]}}],
|
|
555
|
+
"customLogicalOperation": "1"},
|
|
556
|
+
"deliveryConfigurations": {"receivers": [
|
|
557
|
+
{"type": "Notification", "recipients": [e["USER_ID"]]},
|
|
558
|
+
{"type": "Email", "recipients": [e["USER_ID"]]}]},
|
|
559
|
+
}, sys.stdout)
|
|
560
|
+
PY
|
|
561
|
+
|
|
562
|
+
sf api request rest "/services/data/v66.0/tableau/dataAlerts" \
|
|
563
|
+
-X POST -H "Content-Type: application/json" -b "@$body" -o <org> \
|
|
564
|
+
| python3 -m json.tool
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
The response (HTTP 200) returns `id` (the alert id, prefix `3VR…`), and a `metricId` with prefix `1HU…` — the auto-created sub-metric, not the `1DO…` bare metric you sent. Response `filterContext` is `[]` (known bug); confirm the real filter via the sub-metric fetch shown earlier. All enum values in the response are lowercase.
|
|
568
|
+
|
|
569
|
+
### Example: unfiltered alert (all agents)
|
|
570
|
+
|
|
571
|
+
Same body, but `"filterContext": []` and (typically) `minuteLevelFrequency: 60` — no sub-metric is created; the alert references the bare `_mtc` id directly.
|
|
572
|
+
|
|
573
|
+
### Available enum values
|
|
574
|
+
|
|
575
|
+
- **Schedule types:** `"EveryNMinutes"` (+`minuteLevelFrequency`), `"Daily"` (+`hoursOfDay`), `"Weekly"` (+`hoursOfDay`,`daysOfWeek`).
|
|
576
|
+
- **Condition operators:** `"Equals"`, `"NotEqual"`, `"GreaterThan"`, `"GreaterOrEqual"`, `"LessThan"`, `"LessOrEqual"`.
|
|
577
|
+
- **Value types:** `"Metric"` (leftOperand); `"RawValue"` (rightOperand) with `dataType` `"Number"`/`"Text"`/`"Percent"`.
|
|
578
|
+
- **Receiver types:** `"Notification"` / `"Email"` (recipients = user IDs); `"Slack"` (recipients = Slack channel IDs).
|
|
579
|
+
- **Insight types:** `"Popc"` (period-over-period — used by AHM), `"RiskyMonopoly"`, `"Unspecified"`.
|
|
580
|
+
- **Filter operators:** `"Equals"`, `"DoesNotEquals"`, `"LessThan"`, `"GreaterThan"`, `"LessOrEqual"`, `"GreaterOrEqual"`, `"Between"`, `"IsNull"`, `"IsNotNull"`, `"Contains"`, `"DoesNotContain"`, `"StartsWith"`, `"EndsWith"`, `"In"`, `"NotIn"`.
|
|
581
|
+
- **Time-context operators:** `"LastNHours"`, `"LastNDays"`, `"LastNWeeks"`, `"LastNMonths"`, `"LastNQuarters"`, `"LastNYears"`, `"LastNMinutes"`.
|
|
582
|
+
|
|
583
|
+
### Constructing the `utterance` (alert name)
|
|
584
|
+
|
|
585
|
+
Conventional encoded format the AHM UI parses (the backend accepts arbitrary strings):
|
|
586
|
+
|
|
587
|
+
```text
|
|
588
|
+
{freeText}_AHM_{severity}_AHM_{metricApiName}::{sdmApiName}_AHM_{operator}_{threshold}_AHM_{scope}
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
`severity` = `High`/`Medium`/`Low`; `operator` = `Above`/`Below`; `scope` = `All` (no filter) or the filter value. Using this format lets the AHM UI display the alert correctly.
|
|
592
|
+
|
|
593
|
+
### Troubleshooting
|
|
594
|
+
|
|
595
|
+
- **`JSON_PARSER_ERROR: Unrecognized field`** — using a GET-response field name in the POST. Common: `alertName` → use `utterance`; `intervalInMinutes` → `minuteLevelFrequency`.
|
|
596
|
+
- **`JSON_PARSER_ERROR: Could not resolve type id`** — wrong casing on a `type` discriminator. Use PascalCase (`"Metric"`, `"RawValue"`, `"EveryNMinutes"`, …).
|
|
597
|
+
- **`INTERNAL_ERROR: Validation Failed: Invalid calculated Field`** — filter `fieldName` missing the table prefix; use dot notation.
|
|
598
|
+
- **`400` with no clear message** — `content.modelApiNameOrId` must be an **array** (`["2SM…"]`), not a string.
|
|
599
|
+
- **Alert created but never fires** — the agent name in `filterContext.values` must exactly match a DLO value (from the DLO query in "List the agents available for alert filters"). If the agent has never had a session, there's no data to alert on.
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
## Update an alert
|
|
604
|
+
|
|
605
|
+
The PUT API (`PUT /services/data/v66.0/tableau/dataAlerts/{alertId}`) is **not yet available** (expected later 2026). Until then, **delete** the existing alert and **create** a new one with the updated configuration.
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
## Delete an alert
|
|
610
|
+
|
|
611
|
+
```bash
|
|
612
|
+
ALERT_ID="3VRSG0000001kX34AI" # from the list or the create response
|
|
613
|
+
sf api request rest "/services/data/v66.0/tableau/dataAlerts/$ALERT_ID" -X DELETE -o <org> --include
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
Returns **HTTP 204** (No Content) on success — no response body. Use `--include` to see the status line.
|
|
617
|
+
|
|
618
|
+
---
|
|
619
|
+
|
|
620
|
+
## Trigger history — inspect triggered notifications
|
|
621
|
+
|
|
622
|
+
When an alert's condition is met it generates a **system notification** and optionally an **email** (per `deliveryConfigurations.receivers`).
|
|
623
|
+
|
|
624
|
+
### Check notification counts via CLI (org-global signal only)
|
|
625
|
+
|
|
626
|
+
```bash
|
|
627
|
+
sf api request rest "/services/data/v66.0/connect/notifications/status" -o <org> | python3 -m json.tool
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
Returns `unreadCount` and `unseenCount`. **These are org-global counts across *all* notification types — not per-alert, and not AHM-specific.** A non-zero count means *some* notification was generated (it could be any alert or any other notification), so treat it only as a coarse "something fired" signal, never as confirmation that *this* alert fired. For per-alert trigger history, use the UI Incidents tab below.
|
|
631
|
+
|
|
632
|
+
### Other verification paths
|
|
633
|
+
|
|
634
|
+
- **UI (per-alert trigger history)** — `/lightning/n/standard-AgentforceStudio?c__nav=alerts` → **Incidents** tab is the only surface with the full, per-alert notification list and details. Route the user here whenever they need to confirm a *specific* alert fired or see its history.
|
|
635
|
+
- **Email** — if `deliveryConfigurations` includes `"type": "Email"`, the owner receives an email when it fires.
|
|
636
|
+
- **Metric value** — confirm the underlying metric actually crosses the threshold (see next section).
|
|
637
|
+
|
|
638
|
+
### Troubleshooting alerts that don't fire
|
|
639
|
+
|
|
640
|
+
`unreadCount: 0` is a weak signal (it means no *unread* notifications org-wide — already-read ones don't count). Confirm in the Incidents tab that the specific alert has no history, then check:
|
|
641
|
+
1. **Does the SDM return data?** If the semantic gateway (below) returns null/0, the alert evaluation also returns null and the condition is never met.
|
|
642
|
+
2. **Is the schedule running?** Alerts evaluate on their `minuteLevelFrequency`; a freshly created alert may take a few cycles.
|
|
643
|
+
3. **Does the threshold make sense?** A 10% rate with threshold `>= 0.5` (50%) never fires — remember the raw-0–1-ratio scale.
|
|
644
|
+
|
|
645
|
+
---
|
|
646
|
+
|
|
647
|
+
## Verify metric value via Semantic Engine Gateway (condensed)
|
|
648
|
+
|
|
649
|
+
Use this to confirm a metric actually crosses the threshold before concluding an alert is broken. The gateway queries any `_clc` calculated measurement with time grouping / filters / aggregation.
|
|
650
|
+
|
|
651
|
+
**Endpoint:** `POST /services/data/v66.0/semantic-engine/gateway`. Inputs: SDM **record ID** (the `id`, not `apiName`), the `_clc` name (from the metric's `measurementReference.calculatedFieldApiName`), the time dimension (`AI_Agent_Session.Start_Timestamp` for session-level rates), and an ISO date range. Rate values come back as **raw 0–1 ratios**, same scale as alert thresholds.
|
|
652
|
+
|
|
653
|
+
### Single aggregate value (e.g. overall Escalation Rate for the last 24h)
|
|
654
|
+
|
|
655
|
+
```bash
|
|
656
|
+
body=$(mktemp) && chmod 600 "$body"
|
|
657
|
+
trap 'rm -f "$body"' EXIT
|
|
658
|
+
cat > "$body" <<'JSON'
|
|
659
|
+
{
|
|
660
|
+
"structuredSemanticQuery": {
|
|
661
|
+
"fields": [
|
|
662
|
+
{
|
|
663
|
+
"expression": { "semanticField": { "name": "Escalation_Rate_clc" } },
|
|
664
|
+
"alias": "Escalation_Rate",
|
|
665
|
+
"rowGrouping": false,
|
|
666
|
+
"semanticAggregationMethod": "SEMANTIC_AGGREGATION_METHOD_USER_AGG"
|
|
667
|
+
}
|
|
668
|
+
],
|
|
669
|
+
"topNFilter": { "rowsNumber": 1, "sortOrders": [] },
|
|
670
|
+
"options": { "limitOptions": { "limit": 2 }, "sortOrders": [], "grandTotal": false },
|
|
671
|
+
"semantic_context": { "currency": { "id": "" } },
|
|
672
|
+
"flattenFilter": {
|
|
673
|
+
"filters": [
|
|
674
|
+
{ "fieldName": "AI_Agent_Session.Start_Timestamp", "operator": "Between", "value": "2026-05-02T00:00:00.000Z|2026-05-03T00:00:00.000Z" }
|
|
675
|
+
],
|
|
676
|
+
"filterLogic": "1"
|
|
677
|
+
}
|
|
678
|
+
},
|
|
679
|
+
"semanticModelId": "2SMSG000000a6TV4AY"
|
|
680
|
+
}
|
|
681
|
+
JSON
|
|
682
|
+
|
|
683
|
+
sf api request rest "/services/data/v66.0/semantic-engine/gateway" \
|
|
684
|
+
-X POST -H "Content-Type: application/json" -b "@$body" -o <org> \
|
|
685
|
+
| python3 -m json.tool
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
### Time series (per-day) and agent filter
|
|
689
|
+
|
|
690
|
+
For a per-day series, add a first field with `"rowGrouping": true` and a `DATETRUNC('day', [AI_Agent_Session].[Start_Timestamp])` calculated field (alias e.g. `Day`), set `topNFilter.rowsNumber` = N, `options.limitOptions.limit` = N+1, and `grandTotal: true`. To scope to an agent, add a second `flattenFilter.filters` entry `{ "fieldName": "AI_Agent_Session_Participant.AI_Agent_Api_Name", "operator": "In", "value": "te1" }` (comma-separated for multiple) and set `"filterLogic": "1 AND 2"`.
|
|
691
|
+
|
|
692
|
+
Response rows carry `is_data_row__sl` (`true` = data row, `false` = grand total) and `grouping_1__sl` (`0` = detail, `1` = total); the `values` array is indexed by each field's `placeInOrder`.
|
|
693
|
+
|
|
694
|
+
### Discover available `_clc` measurements
|
|
695
|
+
|
|
696
|
+
```bash
|
|
697
|
+
SDM_API_NAME="Service_Agent_Analytics_SDM_1f8"
|
|
698
|
+
sf api request rest "/services/data/v66.0/ssot/semantic/models/${SDM_API_NAME}/calculated-measurements" -o <org> \
|
|
699
|
+
| python3 -c 'import sys,json;[print(m["apiName"],"|",m.get("label",""),"|",m.get("dataType","")) for m in json.load(sys.stdin).get("items",[])]'
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
Common Service SDM measures: `Escalation_Rate_clc`, `Deflection_Rate_clc`, `Engagement_Rate_clc`, `Abandonment_Rate_clc`, `Error_Rate_clc`, `Unique_Sessions_clc`, `Escalated_Sessions_clc`, `Average_Session_Duration_clc`.
|
|
703
|
+
|
|
704
|
+
Troubleshooting:
|
|
705
|
+
- **`null` values** — verify data exists in the DLO (`SELECT COUNT(*) FROM ssot__AiAgentSession__dlm` via `query-sql`). DLO has rows but gateway returns null → the SDM-to-DLO mapping is broken (provisioning issue).
|
|
706
|
+
- **`USER_ILLEGAL_ARGUMENT_RESOLVE_ENTITY_ERROR: Failed to resolve semantic field`** — wrong `_clc` name; list measurements with the discovery command.
|
|
707
|
+
- **`404 Not Found`** — confirm the path is `/services/data/v66.0/semantic-engine/gateway`; if it still 404s, retry at the org's current API version (the resource may be exposed at a different version on that org).
|
|
708
|
+
|
|
709
|
+
---
|
|
710
|
+
|
|
711
|
+
## Global troubleshooting
|
|
712
|
+
|
|
713
|
+
| Error | Cause / fix |
|
|
714
|
+
|---|---|
|
|
715
|
+
| `400 MISSING_PARAM: Owner ID cannot be empty` | The list GET requires `?ownerId={userId}`. |
|
|
716
|
+
| `405 Method Not Allowed` on `/tableau/dataAlerts/{id}` | Single-alert GET unsupported — list and filter by `id` client-side. |
|
|
717
|
+
| `401 Bad_OAuth_Token` | Token expired — `sf api request rest` refreshes automatically on the next call. |
|
|
718
|
+
| `404` on `/wave/dataAlerts` or `/analytics/dataAlerts` | Wrong namespace — AHM alerts live under `/tableau/dataAlerts`. |
|
|
719
|
+
| `FUNCTIONALITY_NOT_ENABLED: [Wave]` | Unrelated — `/tableau/dataAlerts` does not require Wave / CRM Analytics. |
|